Sync with Git 2.16.4
authorJunio C Hamano <gitster@pobox.com>
Tue, 22 May 2018 05:25:26 +0000 (14:25 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 22 May 2018 05:25:26 +0000 (14:25 +0900)
* maint-2.16:
Git 2.16.4
Git 2.15.2
Git 2.14.4
Git 2.13.7
verify_path: disallow symlinks in .gitmodules
update-index: stat updated files earlier
verify_dotfile: mention case-insensitivity in comment
verify_path: drop clever fallthrough
skip_prefix: add case-insensitive variant
is_{hfs,ntfs}_dotgitmodules: add tests
is_ntfs_dotgit: match other .git files
is_hfs_dotgit: match other .git files
is_ntfs_dotgit: use a size_t for traversing string
submodule-config: verify submodule names as paths

1  2 
apply.c
builtin/submodule--helper.c
builtin/update-index.c
cache.h
dir.c
git-compat-util.h
git-submodule.sh
read-cache.c
diff --combined apply.c
index 134dc7ba78cddd99406b78a97898bd8a32393b4c,5a147ced30087de57a17ef351fa4c569694ba4e4..2d1cfe4dbb563e4601f5fec16d6ae2fe88fee80c
+++ b/apply.c
@@@ -950,7 -950,7 +950,7 @@@ static int gitdiff_verify_name(struct a
                }
                free(another);
        } else {
 -              if (!starts_with(line, "/dev/null\n"))
 +              if (!is_dev_null(line))
                        return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
        }
  
@@@ -2263,8 -2263,8 +2263,8 @@@ static void show_stats(struct apply_sta
  static int read_old_data(struct stat *st, struct patch *patch,
                         const char *path, struct strbuf *buf)
  {
 -      enum safe_crlf safe_crlf = patch->crlf_in_old ?
 -              SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
 +      int conv_flags = patch->crlf_in_old ?
 +              CONV_EOL_KEEP_CRLF : CONV_EOL_RENORMALIZE;
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
                if (strbuf_readlink(buf, path, st->st_size) < 0)
                 * should never look at the index when explicit crlf option
                 * is given.
                 */
 -              convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
 +              convert_to_git(NULL, path, buf->buf, buf->len, buf, conv_flags);
                return 0;
        default:
                return -1;
@@@ -2301,7 -2301,7 +2301,7 @@@ static void update_pre_post_images(stru
                                   size_t len, size_t postlen)
  {
        int i, ctx, reduced;
 -      char *new, *old, *fixed;
 +      char *new_buf, *old_buf, *fixed;
        struct image fixed_preimage;
  
        /*
         * We trust the caller to tell us if the update can be done
         * in place (postlen==0) or not.
         */
 -      old = postimage->buf;
 +      old_buf = postimage->buf;
        if (postlen)
 -              new = postimage->buf = xmalloc(postlen);
 +              new_buf = postimage->buf = xmalloc(postlen);
        else
 -              new = old;
 +              new_buf = old_buf;
        fixed = preimage->buf;
  
        for (i = reduced = ctx = 0; i < postimage->nr; i++) {
                size_t l_len = postimage->line[i].len;
                if (!(postimage->line[i].flag & LINE_COMMON)) {
                        /* an added line -- no counterparts in preimage */
 -                      memmove(new, old, l_len);
 -                      old += l_len;
 -                      new += l_len;
 +                      memmove(new_buf, old_buf, l_len);
 +                      old_buf += l_len;
 +                      new_buf += l_len;
                        continue;
                }
  
                /* a common context -- skip it in the original postimage */
 -              old += l_len;
 +              old_buf += l_len;
  
                /* and find the corresponding one in the fixed preimage */
                while (ctx < preimage->nr &&
  
                /* and copy it in, while fixing the line length */
                l_len = preimage->line[ctx].len;
 -              memcpy(new, fixed, l_len);
 -              new += l_len;
 +              memcpy(new_buf, fixed, l_len);
 +              new_buf += l_len;
                fixed += l_len;
                postimage->line[i].len = l_len;
                ctx++;
        }
  
        if (postlen
 -          ? postlen < new - postimage->buf
 -          : postimage->len < new - postimage->buf)
 +          ? postlen < new_buf - postimage->buf
 +          : postimage->len < new_buf - postimage->buf)
                die("BUG: caller miscounted postlen: asked %d, orig = %d, used = %d",
 -                  (int)postlen, (int) postimage->len, (int)(new - postimage->buf));
 +                  (int)postlen, (int) postimage->len, (int)(new_buf - postimage->buf));
  
        /* Fix the length of the whole thing */
 -      postimage->len = new - postimage->buf;
 +      postimage->len = new_buf - postimage->buf;
        postimage->nr -= reduced;
  }
  
  static int line_by_line_fuzzy_match(struct image *img,
                                    struct image *preimage,
                                    struct image *postimage,
 -                                  unsigned long try,
 -                                  int try_lno,
 +                                  unsigned long current,
 +                                  int current_lno,
                                    int preimage_limit)
  {
        int i;
  
        for (i = 0; i < preimage_limit; i++) {
                size_t prelen = preimage->line[i].len;
 -              size_t imglen = img->line[try_lno+i].len;
 +              size_t imglen = img->line[current_lno+i].len;
  
 -              if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
 +              if (!fuzzy_matchlines(img->buf + current + imgoff, imglen,
                                      preimage->buf + preoff, prelen))
                        return 0;
                if (preimage->line[i].flag & LINE_COMMON)
         */
        extra_chars = preimage_end - preimage_eof;
        strbuf_init(&fixed, imgoff + extra_chars);
 -      strbuf_add(&fixed, img->buf + try, imgoff);
 +      strbuf_add(&fixed, img->buf + current, imgoff);
        strbuf_add(&fixed, preimage_eof, extra_chars);
        fixed_buf = strbuf_detach(&fixed, &fixed_len);
        update_pre_post_images(preimage, postimage,
@@@ -2455,8 -2455,8 +2455,8 @@@ static int match_fragment(struct apply_
                          struct image *img,
                          struct image *preimage,
                          struct image *postimage,
 -                        unsigned long try,
 -                        int try_lno,
 +                        unsigned long current,
 +                        int current_lno,
                          unsigned ws_rule,
                          int match_beginning, int match_end)
  {
        size_t fixed_len, postlen;
        int preimage_limit;
  
 -      if (preimage->nr + try_lno <= img->nr) {
 +      if (preimage->nr + current_lno <= img->nr) {
                /*
                 * The hunk falls within the boundaries of img.
                 */
                preimage_limit = preimage->nr;
 -              if (match_end && (preimage->nr + try_lno != img->nr))
 +              if (match_end && (preimage->nr + current_lno != img->nr))
                        return 0;
        } else if (state->ws_error_action == correct_ws_error &&
                   (ws_rule & WS_BLANK_AT_EOF)) {
                 * match with img, and the remainder of the preimage
                 * must be blank.
                 */
 -              preimage_limit = img->nr - try_lno;
 +              preimage_limit = img->nr - current_lno;
        } else {
                /*
                 * The hunk extends beyond the end of the img and
                return 0;
        }
  
 -      if (match_beginning && try_lno)
 +      if (match_beginning && current_lno)
                return 0;
  
        /* Quick hash check */
        for (i = 0; i < preimage_limit; i++)
 -              if ((img->line[try_lno + i].flag & LINE_PATCHED) ||
 -                  (preimage->line[i].hash != img->line[try_lno + i].hash))
 +              if ((img->line[current_lno + i].flag & LINE_PATCHED) ||
 +                  (preimage->line[i].hash != img->line[current_lno + i].hash))
                        return 0;
  
        if (preimage_limit == preimage->nr) {
                /*
                 * Do we have an exact match?  If we were told to match
 -               * at the end, size must be exactly at try+fragsize,
 -               * otherwise try+fragsize must be still within the preimage,
 +               * at the end, size must be exactly at current+fragsize,
 +               * otherwise current+fragsize must be still within the preimage,
                 * and either case, the old piece should match the preimage
                 * exactly.
                 */
                if ((match_end
 -                   ? (try + preimage->len == img->len)
 -                   : (try + preimage->len <= img->len)) &&
 -                  !memcmp(img->buf + try, preimage->buf, preimage->len))
 +                   ? (current + preimage->len == img->len)
 +                   : (current + preimage->len <= img->len)) &&
 +                  !memcmp(img->buf + current, preimage->buf, preimage->len))
                        return 1;
        } else {
                /*
         */
        if (state->ws_ignore_action == ignore_ws_change)
                return line_by_line_fuzzy_match(img, preimage, postimage,
 -                                              try, try_lno, preimage_limit);
 +                                              current, current_lno, preimage_limit);
  
        if (state->ws_error_action != correct_ws_error)
                return 0;
         */
        strbuf_init(&fixed, preimage->len + 1);
        orig = preimage->buf;
 -      target = img->buf + try;
 +      target = img->buf + current;
        for (i = 0; i < preimage_limit; i++) {
                size_t oldlen = preimage->line[i].len;
 -              size_t tgtlen = img->line[try_lno + i].len;
 +              size_t tgtlen = img->line[current_lno + i].len;
                size_t fixstart = fixed.len;
                struct strbuf tgtfix;
                int match;
@@@ -2666,8 -2666,8 +2666,8 @@@ static int find_pos(struct apply_state 
                    int match_beginning, int match_end)
  {
        int i;
 -      unsigned long backwards, forwards, try;
 -      int backwards_lno, forwards_lno, try_lno;
 +      unsigned long backwards, forwards, current;
 +      int backwards_lno, forwards_lno, current_lno;
  
        /*
         * If match_beginning or match_end is specified, there is no
        if ((size_t) line > img->nr)
                line = img->nr;
  
 -      try = 0;
 +      current = 0;
        for (i = 0; i < line; i++)
 -              try += img->line[i].len;
 +              current += img->line[i].len;
  
        /*
         * There's probably some smart way to do this, but I'll leave
         * that to the smart and beautiful people. I'm simple and stupid.
         */
 -      backwards = try;
 +      backwards = current;
        backwards_lno = line;
 -      forwards = try;
 +      forwards = current;
        forwards_lno = line;
 -      try_lno = line;
 +      current_lno = line;
  
        for (i = 0; ; i++) {
                if (match_fragment(state, img, preimage, postimage,
 -                                 try, try_lno, ws_rule,
 +                                 current, current_lno, ws_rule,
                                   match_beginning, match_end))
 -                      return try_lno;
 +                      return current_lno;
  
        again:
                if (backwards_lno == 0 && forwards_lno == img->nr)
                        }
                        backwards_lno--;
                        backwards -= img->line[backwards_lno].len;
 -                      try = backwards;
 -                      try_lno = backwards_lno;
 +                      current = backwards;
 +                      current_lno = backwards_lno;
                } else {
                        if (forwards_lno == img->nr) {
                                i++;
                        }
                        forwards += img->line[forwards_lno].len;
                        forwards_lno++;
 -                      try = forwards;
 -                      try_lno = forwards_lno;
 +                      current = forwards;
 +                      current_lno = forwards_lno;
                }
  
        }
@@@ -3154,7 -3154,7 +3154,7 @@@ static int apply_binary(struct apply_st
                 * See if the old one matches what the patch
                 * applies to.
                 */
 -              hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
 +              hash_object_file(img->buf, img->len, blob_type, &oid);
                if (strcmp(oid_to_hex(&oid), patch->old_sha1_prefix))
                        return error(_("the patch applies to '%s' (%s), "
                                       "which does not match the "
                                     name);
  
                /* verify that the result matches */
 -              hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
 +              hash_object_file(img->buf, img->len, blob_type, &oid);
                if (strcmp(oid_to_hex(&oid), patch->new_sha1_prefix))
                        return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
                                name, patch->new_sha1_prefix, oid_to_hex(&oid));
@@@ -3554,7 -3554,7 +3554,7 @@@ static int try_threeway(struct apply_st
  
        /* Preimage the patch was prepared for */
        if (patch->is_new)
 -              write_sha1_file("", 0, blob_type, pre_oid.hash);
 +              write_object_file("", 0, blob_type, &pre_oid);
        else if (get_oid(patch->old_sha1_prefix, &pre_oid) ||
                 read_blob_object(&buf, &pre_oid, patch->old_mode))
                return error(_("repository lacks the necessary blob to fall back on 3-way merge."));
                return -1;
        }
        /* post_oid is theirs */
 -      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_oid.hash);
 +      write_object_file(tmp_image.buf, tmp_image.len, blob_type, &post_oid);
        clear_image(&tmp_image);
  
        /* our_oid is ours */
                        return error(_("cannot read the current contents of '%s'"),
                                     patch->old_name);
        }
 -      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_oid.hash);
 +      write_object_file(tmp_image.buf, tmp_image.len, blob_type, &our_oid);
        clear_image(&tmp_image);
  
        /* in-core three-way merge between post and our using pre as base */
@@@ -3860,9 -3860,9 +3860,9 @@@ static int check_unsafe_path(struct pat
        if (!patch->is_delete)
                new_name = patch->new_name;
  
-       if (old_name && !verify_path(old_name))
+       if (old_name && !verify_path(old_name, patch->old_mode))
                return error(_("invalid path '%s'"), old_name);
-       if (new_name && !verify_path(new_name))
+       if (new_name && !verify_path(new_name, patch->new_mode))
                return error(_("invalid path '%s'"), new_name);
        return 0;
  }
@@@ -4163,30 -4163,30 +4163,30 @@@ static void show_mode_change(struct pat
  static void show_rename_copy(struct patch *p)
  {
        const char *renamecopy = p->is_rename ? "rename" : "copy";
 -      const char *old, *new;
 +      const char *old_name, *new_name;
  
        /* Find common prefix */
 -      old = p->old_name;
 -      new = p->new_name;
 +      old_name = p->old_name;
 +      new_name = p->new_name;
        while (1) {
                const char *slash_old, *slash_new;
 -              slash_old = strchr(old, '/');
 -              slash_new = strchr(new, '/');
 +              slash_old = strchr(old_name, '/');
 +              slash_new = strchr(new_name, '/');
                if (!slash_old ||
                    !slash_new ||
 -                  slash_old - old != slash_new - new ||
 -                  memcmp(old, new, slash_new - new))
 +                  slash_old - old_name != slash_new - new_name ||
 +                  memcmp(old_name, new_name, slash_new - new_name))
                        break;
 -              old = slash_old + 1;
 -              new = slash_new + 1;
 +              old_name = slash_old + 1;
 +              new_name = slash_new + 1;
        }
 -      /* p->old_name thru old is the common prefix, and old and new
 +      /* p->old_name thru old_name is the common prefix, and old_name and new_name
         * through the end of names are renames
         */
 -      if (old != p->old_name)
 +      if (old_name != p->old_name)
                printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
 -                     (int)(old - p->old_name), p->old_name,
 -                     old, new, p->score);
 +                     (int)(old_name - p->old_name), p->old_name,
 +                     old_name, new_name, p->score);
        else
                printf(" %s %s => %s (%d%%)\n", renamecopy,
                       p->old_name, p->new_name, p->score);
@@@ -4291,7 -4291,7 +4291,7 @@@ static int add_index_file(struct apply_
                        }
                        fill_stat_cache_info(ce, &st);
                }
 -              if (write_sha1_file(buf, size, blob_type, ce->oid.hash) < 0) {
 +              if (write_object_file(buf, size, blob_type, &ce->oid) < 0) {
                        free(ce);
                        return error(_("unable to create backing store "
                                       "for newly created file %s"), path);
@@@ -4943,9 -4943,8 +4943,9 @@@ int apply_parse_options(int argc, cons
                        N_("make sure the patch is applicable to the current index")),
                OPT_BOOL(0, "cached", &state->cached,
                        N_("apply a patch without touching the working tree")),
 -              OPT_BOOL(0, "unsafe-paths", &state->unsafe_paths,
 -                      N_("accept a patch that touches outside the working area")),
 +              OPT_BOOL_F(0, "unsafe-paths", &state->unsafe_paths,
 +                         N_("accept a patch that touches outside the working area"),
 +                         PARSE_OPT_NOCOMPLETE),
                OPT_BOOL(0, "apply", force_apply,
                        N_("also apply the patch (use with --stat/--summary/--check)")),
                OPT_BOOL('3', "3way", &state->threeway,
index 6ba8587b6d3b7b8b1bc7a96451916c60210b093b,9f658a59a2ca9d4f219142870813671ef808ad32..4f35c98bb970328bca5135bd66fdaf3a9fce0d16
@@@ -20,7 -20,6 +20,7 @@@
  #define OPT_QUIET (1 << 0)
  #define OPT_CACHED (1 << 1)
  #define OPT_RECURSIVE (1 << 2)
 +#define OPT_FORCE (1 << 3)
  
  typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
                                  void *cb_data);
@@@ -51,20 -50,6 +51,20 @@@ static char *get_default_remote(void
        return ret;
  }
  
 +static int print_default_remote(int argc, const char **argv, const char *prefix)
 +{
 +      const char *remote;
 +
 +      if (argc != 1)
 +              die(_("submodule--helper print-default-remote takes no arguments"));
 +
 +      remote = get_default_remote();
 +      if (remote)
 +              printf("%s\n", remote);
 +
 +      return 0;
 +}
 +
  static int starts_with_dot_slash(const char *str)
  {
        return str[0] == '.' && is_dir_sep(str[1]);
@@@ -373,25 -358,6 +373,25 @@@ static void module_list_active(struct m
        *list = active_modules;
  }
  
 +static char *get_up_path(const char *path)
 +{
 +      int i;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      for (i = count_slashes(path); i; i--)
 +              strbuf_addstr(&sb, "../");
 +
 +      /*
 +       * Check if 'path' ends with slash or not
 +       * for having the same output for dir/sub_dir
 +       * and dir/sub_dir/
 +       */
 +      if (!is_dir_sep(path[strlen(path) - 1]))
 +              strbuf_addstr(&sb, "../");
 +
 +      return strbuf_detach(&sb, NULL);
 +}
 +
  static int module_list(int argc, const char **argv, const char *prefix)
  {
        int i;
@@@ -752,309 -718,6 +752,309 @@@ static int module_name(int argc, const 
        return 0;
  }
  
 +struct sync_cb {
 +      const char *prefix;
 +      unsigned int flags;
 +};
 +
 +#define SYNC_CB_INIT { NULL, 0 }
 +
 +static void sync_submodule(const char *path, const char *prefix,
 +                         unsigned int flags)
 +{
 +      const struct submodule *sub;
 +      char *remote_key = NULL;
 +      char *sub_origin_url, *super_config_url, *displaypath;
 +      struct strbuf sb = STRBUF_INIT;
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      char *sub_config_path = NULL;
 +
 +      if (!is_submodule_active(the_repository, path))
 +              return;
 +
 +      sub = submodule_from_path(&null_oid, path);
 +
 +      if (sub && sub->url) {
 +              if (starts_with_dot_dot_slash(sub->url) ||
 +                  starts_with_dot_slash(sub->url)) {
 +                      char *remote_url, *up_path;
 +                      char *remote = get_default_remote();
 +                      strbuf_addf(&sb, "remote.%s.url", remote);
 +
 +                      if (git_config_get_string(sb.buf, &remote_url))
 +                              remote_url = xgetcwd();
 +
 +                      up_path = get_up_path(path);
 +                      sub_origin_url = relative_url(remote_url, sub->url, up_path);
 +                      super_config_url = relative_url(remote_url, sub->url, NULL);
 +
 +                      free(remote);
 +                      free(up_path);
 +                      free(remote_url);
 +              } else {
 +                      sub_origin_url = xstrdup(sub->url);
 +                      super_config_url = xstrdup(sub->url);
 +              }
 +      } else {
 +              sub_origin_url = xstrdup("");
 +              super_config_url = xstrdup("");
 +      }
 +
 +      displaypath = get_submodule_displaypath(path, prefix);
 +
 +      if (!(flags & OPT_QUIET))
 +              printf(_("Synchronizing submodule url for '%s'\n"),
 +                       displaypath);
 +
 +      strbuf_reset(&sb);
 +      strbuf_addf(&sb, "submodule.%s.url", sub->name);
 +      if (git_config_set_gently(sb.buf, super_config_url))
 +              die(_("failed to register url for submodule path '%s'"),
 +                    displaypath);
 +
 +      if (!is_submodule_populated_gently(path, NULL))
 +              goto cleanup;
 +
 +      prepare_submodule_repo_env(&cp.env_array);
 +      cp.git_cmd = 1;
 +      cp.dir = path;
 +      argv_array_pushl(&cp.args, "submodule--helper",
 +                       "print-default-remote", NULL);
 +
 +      strbuf_reset(&sb);
 +      if (capture_command(&cp, &sb, 0))
 +              die(_("failed to get the default remote for submodule '%s'"),
 +                    path);
 +
 +      strbuf_strip_suffix(&sb, "\n");
 +      remote_key = xstrfmt("remote.%s.url", sb.buf);
 +
 +      strbuf_reset(&sb);
 +      submodule_to_gitdir(&sb, path);
 +      strbuf_addstr(&sb, "/config");
 +
 +      if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url))
 +              die(_("failed to update remote for submodule '%s'"),
 +                    path);
 +
 +      if (flags & OPT_RECURSIVE) {
 +              struct child_process cpr = CHILD_PROCESS_INIT;
 +
 +              cpr.git_cmd = 1;
 +              cpr.dir = path;
 +              prepare_submodule_repo_env(&cpr.env_array);
 +
 +              argv_array_push(&cpr.args, "--super-prefix");
 +              argv_array_pushf(&cpr.args, "%s/", displaypath);
 +              argv_array_pushl(&cpr.args, "submodule--helper", "sync",
 +                               "--recursive", NULL);
 +
 +              if (flags & OPT_QUIET)
 +                      argv_array_push(&cpr.args, "--quiet");
 +
 +              if (run_command(&cpr))
 +                      die(_("failed to recurse into submodule '%s'"),
 +                            path);
 +      }
 +
 +cleanup:
 +      free(super_config_url);
 +      free(sub_origin_url);
 +      strbuf_release(&sb);
 +      free(remote_key);
 +      free(displaypath);
 +      free(sub_config_path);
 +}
 +
 +static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data)
 +{
 +      struct sync_cb *info = cb_data;
 +      sync_submodule(list_item->name, info->prefix, info->flags);
 +
 +}
 +
 +static int module_sync(int argc, const char **argv, const char *prefix)
 +{
 +      struct sync_cb info = SYNC_CB_INIT;
 +      struct pathspec pathspec;
 +      struct module_list list = MODULE_LIST_INIT;
 +      int quiet = 0;
 +      int recursive = 0;
 +
 +      struct option module_sync_options[] = {
 +              OPT__QUIET(&quiet, N_("Suppress output of synchronizing submodule url")),
 +              OPT_BOOL(0, "recursive", &recursive,
 +                      N_("Recurse into nested submodules")),
 +              OPT_END()
 +      };
 +
 +      const char *const git_submodule_helper_usage[] = {
 +              N_("git submodule--helper sync [--quiet] [--recursive] [<path>]"),
 +              NULL
 +      };
 +
 +      argc = parse_options(argc, argv, prefix, module_sync_options,
 +                           git_submodule_helper_usage, 0);
 +
 +      if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
 +              return 1;
 +
 +      info.prefix = prefix;
 +      if (quiet)
 +              info.flags |= OPT_QUIET;
 +      if (recursive)
 +              info.flags |= OPT_RECURSIVE;
 +
 +      for_each_listed_submodule(&list, sync_submodule_cb, &info);
 +
 +      return 0;
 +}
 +
 +struct deinit_cb {
 +      const char *prefix;
 +      unsigned int flags;
 +};
 +#define DEINIT_CB_INIT { NULL, 0 }
 +
 +static void deinit_submodule(const char *path, const char *prefix,
 +                           unsigned int flags)
 +{
 +      const struct submodule *sub;
 +      char *displaypath = NULL;
 +      struct child_process cp_config = CHILD_PROCESS_INIT;
 +      struct strbuf sb_config = STRBUF_INIT;
 +      char *sub_git_dir = xstrfmt("%s/.git", path);
 +
 +      sub = submodule_from_path(&null_oid, path);
 +
 +      if (!sub || !sub->name)
 +              goto cleanup;
 +
 +      displaypath = get_submodule_displaypath(path, prefix);
 +
 +      /* remove the submodule work tree (unless the user already did it) */
 +      if (is_directory(path)) {
 +              struct strbuf sb_rm = STRBUF_INIT;
 +              const char *format;
 +
 +              /*
 +               * protect submodules containing a .git directory
 +               * NEEDSWORK: instead of dying, automatically call
 +               * absorbgitdirs and (possibly) warn.
 +               */
 +              if (is_directory(sub_git_dir))
 +                      die(_("Submodule work tree '%s' contains a .git "
 +                            "directory (use 'rm -rf' if you really want "
 +                            "to remove it including all of its history)"),
 +                          displaypath);
 +
 +              if (!(flags & OPT_FORCE)) {
 +                      struct child_process cp_rm = CHILD_PROCESS_INIT;
 +                      cp_rm.git_cmd = 1;
 +                      argv_array_pushl(&cp_rm.args, "rm", "-qn",
 +                                       path, NULL);
 +
 +                      if (run_command(&cp_rm))
 +                              die(_("Submodule work tree '%s' contains local "
 +                                    "modifications; use '-f' to discard them"),
 +                                    displaypath);
 +              }
 +
 +              strbuf_addstr(&sb_rm, path);
 +
 +              if (!remove_dir_recursively(&sb_rm, 0))
 +                      format = _("Cleared directory '%s'\n");
 +              else
 +                      format = _("Could not remove submodule work tree '%s'\n");
 +
 +              if (!(flags & OPT_QUIET))
 +                      printf(format, displaypath);
 +
 +              strbuf_release(&sb_rm);
 +      }
 +
 +      if (mkdir(path, 0777))
 +              printf(_("could not create empty submodule directory %s"),
 +                    displaypath);
 +
 +      cp_config.git_cmd = 1;
 +      argv_array_pushl(&cp_config.args, "config", "--get-regexp", NULL);
 +      argv_array_pushf(&cp_config.args, "submodule.%s\\.", sub->name);
 +
 +      /* remove the .git/config entries (unless the user already did it) */
 +      if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) {
 +              char *sub_key = xstrfmt("submodule.%s", sub->name);
 +              /*
 +               * remove the whole section so we have a clean state when
 +               * the user later decides to init this submodule again
 +               */
 +              git_config_rename_section_in_file(NULL, sub_key, NULL);
 +              if (!(flags & OPT_QUIET))
 +                      printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"),
 +                               sub->name, sub->url, displaypath);
 +              free(sub_key);
 +      }
 +
 +cleanup:
 +      free(displaypath);
 +      free(sub_git_dir);
 +      strbuf_release(&sb_config);
 +}
 +
 +static void deinit_submodule_cb(const struct cache_entry *list_item,
 +                              void *cb_data)
 +{
 +      struct deinit_cb *info = cb_data;
 +      deinit_submodule(list_item->name, info->prefix, info->flags);
 +}
 +
 +static int module_deinit(int argc, const char **argv, const char *prefix)
 +{
 +      struct deinit_cb info = DEINIT_CB_INIT;
 +      struct pathspec pathspec;
 +      struct module_list list = MODULE_LIST_INIT;
 +      int quiet = 0;
 +      int force = 0;
 +      int all = 0;
 +
 +      struct option module_deinit_options[] = {
 +              OPT__QUIET(&quiet, N_("Suppress submodule status output")),
 +              OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes"), 0),
 +              OPT_BOOL(0, "all", &all, N_("Unregister all submodules")),
 +              OPT_END()
 +      };
 +
 +      const char *const git_submodule_helper_usage[] = {
 +              N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
 +              NULL
 +      };
 +
 +      argc = parse_options(argc, argv, prefix, module_deinit_options,
 +                           git_submodule_helper_usage, 0);
 +
 +      if (all && argc) {
 +              error("pathspec and --all are incompatible");
 +              usage_with_options(git_submodule_helper_usage,
 +                                 module_deinit_options);
 +      }
 +
 +      if (!argc && !all)
 +              die(_("Use '--all' if you really want to deinitialize all submodules"));
 +
 +      if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
 +              return 1;
 +
 +      info.prefix = prefix;
 +      if (quiet)
 +              info.flags |= OPT_QUIET;
 +      if (force)
 +              info.flags |= OPT_FORCE;
 +
 +      for_each_listed_submodule(&list, deinit_submodule_cb, &info);
 +
 +      return 0;
 +}
 +
  static int clone_submodule(const char *path, const char *gitdir, const char *url,
                           const char *depth, struct string_list *reference,
                           int quiet, int progress)
@@@ -1817,6 -1480,29 +1817,29 @@@ static int is_active(int argc, const ch
        return !is_submodule_active(the_repository, argv[1]);
  }
  
+ /*
+  * Exit non-zero if any of the submodule names given on the command line is
+  * invalid. If no names are given, filter stdin to print only valid names
+  * (which is primarily intended for testing).
+  */
+ static int check_name(int argc, const char **argv, const char *prefix)
+ {
+       if (argc > 1) {
+               while (*++argv) {
+                       if (check_submodule_name(*argv) < 0)
+                               return 1;
+               }
+       } else {
+               struct strbuf buf = STRBUF_INIT;
+               while (strbuf_getline(&buf, stdin) != EOF) {
+                       if (!check_submodule_name(buf.buf))
+                               printf("%s\n", buf.buf);
+               }
+               strbuf_release(&buf);
+       }
+       return 0;
+ }
  #define SUPPORT_SUPER_PREFIX (1<<0)
  
  struct cmd_struct {
@@@ -1835,13 -1521,11 +1858,14 @@@ static struct cmd_struct commands[] = 
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
        {"init", module_init, SUPPORT_SUPER_PREFIX},
        {"status", module_status, SUPPORT_SUPER_PREFIX},
 +      {"print-default-remote", print_default_remote, 0},
 +      {"sync", module_sync, SUPPORT_SUPER_PREFIX},
 +      {"deinit", module_deinit, 0},
        {"remote-branch", resolve_remote_submodule_branch, 0},
        {"push-check", push_check, 0},
        {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
        {"is-active", is_active, 0},
+       {"check-name", check_name, 0},
  };
  
  int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --combined builtin/update-index.c
index 58d1c2d2827d61899d73f1ea7632c5ee219f3ace,52eaebae911ee3439f4464c9c317f1776f89c10a..1af8a00b889c968634389bf61138200d97eb32f9
@@@ -364,10 -364,9 +364,9 @@@ static int process_directory(const cha
        return error("%s: is a directory - add files inside instead", path);
  }
  
- static int process_path(const char *path)
+ static int process_path(const char *path, struct stat *st, int stat_errno)
  {
        int pos, len;
-       struct stat st;
        const struct cache_entry *ce;
  
        len = strlen(path);
         * First things first: get the stat information, to decide
         * what to do about the pathname!
         */
-       if (lstat(path, &st) < 0)
-               return process_lstat_error(path, errno);
+       if (stat_errno)
+               return process_lstat_error(path, stat_errno);
  
-       if (S_ISDIR(st.st_mode))
-               return process_directory(path, len, &st);
+       if (S_ISDIR(st->st_mode))
+               return process_directory(path, len, st);
  
-       return add_one_path(ce, path, len, &st);
+       return add_one_path(ce, path, len, st);
  }
  
  static int add_cacheinfo(unsigned int mode, const struct object_id *oid,
        int size, len, option;
        struct cache_entry *ce;
  
-       if (!verify_path(path))
+       if (!verify_path(path, mode))
                return error("Invalid path '%s'", path);
  
        len = strlen(path);
@@@ -449,7 -448,17 +448,18 @@@ static void chmod_path(char flip, cons
  
  static void update_one(const char *path)
  {
-       if (!verify_path(path)) {
+       int stat_errno = 0;
+       struct stat st;
 -      if (mark_valid_only || mark_skip_worktree_only || force_remove)
++      if (mark_valid_only || mark_skip_worktree_only || force_remove ||
++          mark_fsmonitor_only)
+               st.st_mode = 0;
+       else if (lstat(path, &st) < 0) {
+               st.st_mode = 0;
+               stat_errno = errno;
+       } /* else stat is valid */
+       if (!verify_path(path, st.st_mode)) {
                fprintf(stderr, "Ignoring path %s\n", path);
                return;
        }
                report("remove '%s'", path);
                return;
        }
-       if (process_path(path))
+       if (process_path(path, &st, stat_errno))
                die("Unable to process path %s", path);
        report("add '%s'", path);
  }
@@@ -545,7 -554,7 +555,7 @@@ static void read_index_info(int nul_ter
                        path_name = uq.buf;
                }
  
-               if (!verify_path(path_name)) {
+               if (!verify_path(path_name, mode)) {
                        fprintf(stderr, "Ignoring path %s\n", path_name);
                        continue;
                }
diff --combined cache.h
index a61b2d3f0d79b0f56992e0343803811f5265d716,4c29dd02e81ab5e5525c157657534b54cacae249..0323853c99e75a1edc2e4415db9490ae36bd2c0f
+++ b/cache.h
@@@ -4,7 -4,7 +4,7 @@@
  #include "git-compat-util.h"
  #include "strbuf.h"
  #include "hashmap.h"
 -#include "mru.h"
 +#include "list.h"
  #include "advice.h"
  #include "gettext.h"
  #include "convert.h"
  #include "sha1-array.h"
  #include "repository.h"
  
 -#ifndef platform_SHA_CTX
 -/*
 - * platform's underlying implementation of SHA-1; could be OpenSSL,
 - * blk_SHA, Apple CommonCrypto, etc...  Note that including
 - * SHA1_HEADER may have already defined platform_SHA_CTX for our
 - * own implementations like block-sha1 and ppc-sha1, so we list
 - * the default for OpenSSL compatible SHA-1 implementations here.
 - */
 -#define platform_SHA_CTX      SHA_CTX
 -#define platform_SHA1_Init    SHA1_Init
 -#define platform_SHA1_Update  SHA1_Update
 -#define platform_SHA1_Final           SHA1_Final
 -#endif
 -
 -#define git_SHA_CTX           platform_SHA_CTX
 -#define git_SHA1_Init         platform_SHA1_Init
 -#define git_SHA1_Update               platform_SHA1_Update
 -#define git_SHA1_Final                platform_SHA1_Final
 -
 -#ifdef SHA1_MAX_BLOCK_SIZE
 -#include "compat/sha1-chunked.h"
 -#undef git_SHA1_Update
 -#define git_SHA1_Update               git_SHA1_Update_Chunked
 -#endif
 -
  #include <zlib.h>
  typedef struct git_zstream {
        z_stream z;
@@@ -599,7 -624,6 +599,7 @@@ extern int read_index_unmerged(struct i
  
  /* For use with `write_locked_index()`. */
  #define COMMIT_LOCK           (1 << 0)
 +#define SKIP_IF_UNCHANGED     (1 << 1)
  
  /*
   * Write the index while holding an already-taken lock. Close the lock,
   * With `COMMIT_LOCK`, the lock is always committed or rolled back.
   * Without it, the lock is closed, but neither committed nor rolled
   * back.
 + *
 + * If `SKIP_IF_UNCHANGED` is given and the index is unchanged, nothing
 + * is written (and the lock is rolled back if `COMMIT_LOCK` is given).
   */
  extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
  
@@@ -634,7 -655,7 +634,7 @@@ extern int unmerged_index(const struct 
   */
  extern int index_has_changes(struct strbuf *sb);
  
- extern int verify_path(const char *path);
+ extern int verify_path(const char *path, unsigned mode);
  extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
  extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
  extern void adjust_dirname_case(struct index_state *istate, char *name);
@@@ -895,13 -916,10 +895,13 @@@ extern int grafts_replace_parents
  #define GIT_REPO_VERSION 0
  #define GIT_REPO_VERSION_READ 1
  extern int repository_format_precious_objects;
 +extern char *repository_format_partial_clone;
 +extern const char *core_partial_clone_filter_default;
  
  struct repository_format {
        int version;
        int precious_objects;
 +      char *partial_clone; /* value of extensions.partialclone */
        int is_bare;
        int hash_algo;
        char *work_tree;
@@@ -941,10 -959,12 +941,10 @@@ extern void check_repository_format(voi
  #define TYPE_CHANGED    0x0040
  
  /*
 - * Return the name of the file in the local object database that would
 - * be used to store a loose object with the specified sha1.  The
 - * return value is a pointer to a statically allocated buffer that is
 - * overwritten each time the function is called.
 + * Put in `buf` the name of the file in the local object database that
 + * would be used to store a loose object with the specified sha1.
   */
 -extern const char *sha1_file_name(const unsigned char *sha1);
 +extern void sha1_file_name(struct strbuf *buf, const unsigned char *sha1);
  
  /*
   * Return an abbreviated sha1 unique within this repository's object database.
@@@ -1011,7 -1031,7 +1011,7 @@@ static inline void hashclr(unsigned cha
  
  static inline void oidclr(struct object_id *oid)
  {
 -      hashclr(oid->hash);
 +      memset(oid->hash, 0, GIT_MAX_RAWSZ);
  }
  
  
@@@ -1029,6 -1049,8 +1029,6 @@@ extern const struct object_id empty_tre
        "\xe6\x9d\xe2\x9b\xb2\xd1\xd6\x43\x4b\x8b" \
        "\x29\xae\x77\x5a\xd8\xc2\xe4\x8c\x53\x91"
  extern const struct object_id empty_blob_oid;
 -#define EMPTY_BLOB_SHA1_BIN (empty_blob_oid.hash)
 -
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
@@@ -1165,7 -1187,15 +1165,15 @@@ int normalize_path_copy(char *dst, cons
  int longest_ancestor_length(const char *path, struct string_list *prefixes);
  char *strip_path_suffix(const char *path, const char *suffix);
  int daemon_avoid_alias(const char *path);
- extern int is_ntfs_dotgit(const char *name);
+ /*
+  * These functions match their is_hfs_dotgit() counterparts; see utf8.h for
+  * details.
+  */
+ int is_ntfs_dotgit(const char *name);
+ int is_ntfs_dotgitmodules(const char *name);
+ int is_ntfs_dotgitignore(const char *name);
+ int is_ntfs_dotgitattributes(const char *name);
  
  /*
   * Returns true iff "str" could be confused as a command-line option when
@@@ -1218,22 -1248,11 +1226,22 @@@ static inline const unsigned char *look
  
  /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
  extern int sha1_object_info(const unsigned char *, unsigned long *);
 -extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
 -extern int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
 -extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, struct object_id *oid, unsigned flags);
 -extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
 -extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 +
 +extern int hash_object_file(const void *buf, unsigned long len,
 +                          const char *type, struct object_id *oid);
 +
 +extern int write_object_file(const void *buf, unsigned long len,
 +                           const char *type, struct object_id *oid);
 +
 +extern int hash_object_file_literally(const void *buf, unsigned long len,
 +                                    const char *type, struct object_id *oid,
 +                                    unsigned flags);
 +
 +extern int pretend_object_file(void *, unsigned long, enum object_type,
 +                             struct object_id *oid);
 +
 +extern int force_object_loose(const struct object_id *oid, time_t mtime);
 +
  extern int git_open_cloexec(const char *name, int flags);
  #define git_open(name) git_open_cloexec(name, O_RDONLY)
  extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
@@@ -1626,7 -1645,6 +1634,7 @@@ struct pack_window 
  
  extern struct packed_git {
        struct packed_git *next;
 +      struct list_head mru;
        struct pack_window *windows;
        off_t pack_size;
        const void *index_data;
        unsigned pack_local:1,
                 pack_keep:1,
                 freshened:1,
 -               do_not_close:1;
 +               do_not_close:1,
 +               pack_promisor:1;
        unsigned char sha1[20];
        struct revindex_entry *revindex;
        /* something like ".git/objects/pack/xxxxx.pack" */
  } *packed_git;
  
  /*
 - * A most-recently-used ordered version of the packed_git list, which can
 - * be iterated instead of packed_git (and marked via mru_mark).
 + * A most-recently-used ordered version of the packed_git list.
   */
 -extern struct mru packed_git_mru;
 +extern struct list_head packed_git_mru;
  
  struct pack_entry {
        off_t offset;
   * usual "XXXXXX" trailer, and the resulting filename is written into the
   * "template" buffer. Returns the open descriptor.
   */
 -extern int odb_mkstemp(struct strbuf *template, const char *pattern);
 +extern int odb_mkstemp(struct strbuf *temp_filename, const char *pattern);
  
  /*
   * Create a pack .keep file named "name" (which should generally be the output
@@@ -1736,7 -1754,7 +1744,7 @@@ struct object_info 
        unsigned long *sizep;
        off_t *disk_sizep;
        unsigned char *delta_base_sha1;
 -      struct strbuf *typename;
 +      struct strbuf *type_name;
        void **contentp;
  
        /* Response */
  #define OBJECT_INFO_QUICK 8
  extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
  
 +/*
 + * Set this to 0 to prevent sha1_object_info_extended() from fetching missing
 + * blobs. This has a difference only if extensions.partialClone is set.
 + *
 + * Its default value is 1.
 + */
 +extern int fetch_if_missing;
 +
  /* Dumb servers support */
  extern int update_server_info(int);
  
diff --combined dir.c
index dedbf5d476f207e39c1b7853ec8c97553181e5fb,7c4b45e30e0ac87527ff0ef3fd8c90670a1e2064..41aac3b7b38e76bb790da4c21c362d7d0f1d1a07
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -231,10 -231,12 +231,10 @@@ int within_depth(const char *name, int 
   *     1 along with { data, size } of the (possibly augmented) buffer
   *       when successful.
   *
 - * Optionally updates the given sha1_stat with the given OID (when valid).
 + * Optionally updates the given oid_stat with the given OID (when valid).
   */
 -static int do_read_blob(const struct object_id *oid,
 -                      struct sha1_stat *sha1_stat,
 -                      size_t *size_out,
 -                      char **data_out)
 +static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
 +                      size_t *size_out, char **data_out)
  {
        enum object_type type;
        unsigned long sz;
                return -1;
        }
  
 -      if (sha1_stat) {
 -              memset(&sha1_stat->stat, 0, sizeof(sha1_stat->stat));
 -              hashcpy(sha1_stat->sha1, oid->hash);
 +      if (oid_stat) {
 +              memset(&oid_stat->stat, 0, sizeof(oid_stat->stat));
 +              oidcpy(&oid_stat->oid, oid);
        }
  
        if (sz == 0) {
@@@ -652,8 -654,9 +652,8 @@@ void add_exclude(const char *string, co
  
  static int read_skip_worktree_file_from_index(const struct index_state *istate,
                                              const char *path,
 -                                            size_t *size_out,
 -                                            char **data_out,
 -                                            struct sha1_stat *sha1_stat)
 +                                            size_t *size_out, char **data_out,
 +                                            struct oid_stat *oid_stat)
  {
        int pos, len;
  
        if (!ce_skip_worktree(istate->cache[pos]))
                return -1;
  
 -      return do_read_blob(&istate->cache[pos]->oid, sha1_stat, size_out, data_out);
 +      return do_read_blob(&istate->cache[pos]->oid, oid_stat, size_out, data_out);
  }
  
  /*
@@@ -744,8 -747,8 +744,8 @@@ static struct untracked_cache_dir *look
        FLEX_ALLOC_MEM(d, name, name, len);
  
        ALLOC_GROW(dir->dirs, dir->dirs_nr + 1, dir->dirs_alloc);
 -      memmove(dir->dirs + first + 1, dir->dirs + first,
 -              (dir->dirs_nr - first) * sizeof(*dir->dirs));
 +      MOVE_ARRAY(dir->dirs + first + 1, dir->dirs + first,
 +                 dir->dirs_nr - first);
        dir->dirs_nr++;
        dir->dirs[first] = d;
        return d;
@@@ -771,16 -774,7 +771,16 @@@ static void invalidate_directory(struc
                                 struct untracked_cache_dir *dir)
  {
        int i;
 -      uc->dir_invalidated++;
 +
 +      /*
 +       * Invalidation increment here is just roughly correct. If
 +       * untracked_nr or any of dirs[].recurse is non-zero, we
 +       * should increment dir_invalidated too. But that's more
 +       * expensive to do.
 +       */
 +      if (dir->valid)
 +              uc->dir_invalidated++;
 +
        dir->valid = 0;
        dir->untracked_nr = 0;
        for (i = 0; i < dir->dirs_nr; i++)
@@@ -801,8 -795,9 +801,8 @@@ static int add_excludes_from_buffer(cha
   * ss_valid is non-zero, "ss" must contain good value as input.
   */
  static int add_excludes(const char *fname, const char *base, int baselen,
 -                      struct exclude_list *el,
 -                      struct index_state *istate,
 -                      struct sha1_stat *sha1_stat)
 +                      struct exclude_list *el, struct index_state *istate,
 +                      struct oid_stat *oid_stat)
  {
        struct stat st;
        int r;
                        return -1;
                r = read_skip_worktree_file_from_index(istate, fname,
                                                       &size, &buf,
 -                                                     sha1_stat);
 +                                                     oid_stat);
                if (r != 1)
                        return r;
        } else {
                size = xsize_t(st.st_size);
                if (size == 0) {
 -                      if (sha1_stat) {
 -                              fill_stat_data(&sha1_stat->stat, &st);
 -                              hashcpy(sha1_stat->sha1, EMPTY_BLOB_SHA1_BIN);
 -                              sha1_stat->valid = 1;
 +                      if (oid_stat) {
 +                              fill_stat_data(&oid_stat->stat, &st);
 +                              oidcpy(&oid_stat->oid, &empty_blob_oid);
 +                              oid_stat->valid = 1;
                        }
                        close(fd);
                        return 0;
                }
                buf[size++] = '\n';
                close(fd);
 -              if (sha1_stat) {
 +              if (oid_stat) {
                        int pos;
 -                      if (sha1_stat->valid &&
 -                          !match_stat_data_racy(istate, &sha1_stat->stat, &st))
 +                      if (oid_stat->valid &&
 +                          !match_stat_data_racy(istate, &oid_stat->stat, &st))
                                ; /* no content change, ss->sha1 still good */
                        else if (istate &&
                                 (pos = index_name_pos(istate, fname, strlen(fname))) >= 0 &&
                                 !ce_stage(istate->cache[pos]) &&
                                 ce_uptodate(istate->cache[pos]) &&
                                 !would_convert_to_git(istate, fname))
 -                              hashcpy(sha1_stat->sha1,
 -                                      istate->cache[pos]->oid.hash);
 +                              oidcpy(&oid_stat->oid,
 +                                     &istate->cache[pos]->oid);
                        else
 -                              hash_sha1_file(buf, size, "blob", sha1_stat->sha1);
 -                      fill_stat_data(&sha1_stat->stat, &st);
 -                      sha1_stat->valid = 1;
 +                              hash_object_file(buf, size, "blob",
 +                                               &oid_stat->oid);
 +                      fill_stat_data(&oid_stat->stat, &st);
 +                      oid_stat->valid = 1;
                }
        }
  
@@@ -936,7 -930,7 +936,7 @@@ struct exclude_list *add_exclude_list(s
   * Used to set up core.excludesfile and .git/info/exclude lists.
   */
  static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
 -                                   struct sha1_stat *sha1_stat)
 +                                   struct oid_stat *oid_stat)
  {
        struct exclude_list *el;
        /*
        if (!dir->untracked)
                dir->unmanaged_exclude_files++;
        el = add_exclude_list(dir, EXC_FILE, fname);
 -      if (add_excludes(fname, "", 0, el, NULL, sha1_stat) < 0)
 +      if (add_excludes(fname, "", 0, el, NULL, oid_stat) < 0)
                die("cannot use %s as an exclude file", fname);
  }
  
@@@ -1186,7 -1180,7 +1186,7 @@@ static void prep_exclude(struct dir_str
  
        while (current < baselen) {
                const char *cp;
 -              struct sha1_stat sha1_stat;
 +              struct oid_stat oid_stat;
  
                stk = xcalloc(1, sizeof(*stk));
                if (current < 0) {
                }
  
                /* Try to read per-directory file */
 -              hashclr(sha1_stat.sha1);
 -              sha1_stat.valid = 0;
 +              oidclr(&oid_stat.oid);
 +              oid_stat.valid = 0;
                if (dir->exclude_per_dir &&
                    /*
                     * If we know that no files have been added in
                        strbuf_addstr(&sb, dir->exclude_per_dir);
                        el->src = strbuf_detach(&sb, NULL);
                        add_excludes(el->src, el->src, stk->baselen, el, istate,
 -                                   untracked ? &sha1_stat : NULL);
 +                                   untracked ? &oid_stat : NULL);
                }
                /*
                 * NEEDSWORK: when untracked cache is enabled, prep_exclude()
                 * order, though, if you do that.
                 */
                if (untracked &&
 -                  hashcmp(sha1_stat.sha1, untracked->exclude_sha1)) {
 +                  hashcmp(oid_stat.oid.hash, untracked->exclude_sha1)) {
                        invalidate_gitignore(dir->untracked, untracked);
 -                      hashcpy(untracked->exclude_sha1, sha1_stat.sha1);
 +                      hashcpy(untracked->exclude_sha1, oid_stat.oid.hash);
                }
                dir->exclude_stack = stk;
                current = stk->baselen;
@@@ -1779,7 -1773,7 +1779,7 @@@ static enum path_treatment treat_path(s
        if (!de)
                return treat_path_fast(dir, untracked, cdir, istate, path,
                                       baselen, pathspec);
 -      if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
 +      if (is_dot_or_dotdot(de->d_name) || !fspathcmp(de->d_name, ".git"))
                return path_none;
        strbuf_setlen(path, baselen);
        strbuf_addstr(path, de->d_name);
@@@ -1815,19 -1809,24 +1815,19 @@@ static int valid_cached_dir(struct dir_
         */
        refresh_fsmonitor(istate);
        if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
 -              if (stat(path->len ? path->buf : ".", &st)) {
 -                      invalidate_directory(dir->untracked, untracked);
 +              if (lstat(path->len ? path->buf : ".", &st)) {
                        memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
                        return 0;
                }
                if (!untracked->valid ||
                        match_stat_data_racy(istate, &untracked->stat_data, &st)) {
 -                      if (untracked->valid)
 -                              invalidate_directory(dir->untracked, untracked);
                        fill_stat_data(&untracked->stat_data, &st);
                        return 0;
                }
        }
  
 -      if (untracked->check_only != !!check_only) {
 -              invalidate_directory(dir->untracked, untracked);
 +      if (untracked->check_only != !!check_only)
                return 0;
 -      }
  
        /*
         * prep_exclude will be called eventually on this directory,
@@@ -1854,20 -1853,13 +1854,20 @@@ static int open_cached_dir(struct cache
                           struct strbuf *path,
                           int check_only)
  {
 +      const char *c_path;
 +
        memset(cdir, 0, sizeof(*cdir));
        cdir->untracked = untracked;
        if (valid_cached_dir(dir, untracked, istate, path, check_only))
                return 0;
 -      cdir->fdir = opendir(path->len ? path->buf : ".");
 -      if (dir->untracked)
 +      c_path = path->len ? path->buf : ".";
 +      cdir->fdir = opendir(c_path);
 +      if (!cdir->fdir)
 +              warning_errno(_("could not open directory '%s'"), c_path);
 +      if (dir->untracked) {
 +              invalidate_directory(dir->untracked, untracked);
                dir->untracked->dir_opened++;
 +      }
        if (!cdir->fdir)
                return -1;
        return 0;
@@@ -2172,13 -2164,8 +2172,13 @@@ static struct untracked_cache_dir *vali
                                                      const struct pathspec *pathspec)
  {
        struct untracked_cache_dir *root;
 +      static int untracked_cache_disabled = -1;
  
 -      if (!dir->untracked || getenv("GIT_DISABLE_UNTRACKED_CACHE"))
 +      if (!dir->untracked)
 +              return NULL;
 +      if (untracked_cache_disabled < 0)
 +              untracked_cache_disabled = git_env_bool("GIT_DISABLE_UNTRACKED_CACHE", 0);
 +      if (untracked_cache_disabled)
                return NULL;
  
        /*
  
        /* Validate $GIT_DIR/info/exclude and core.excludesfile */
        root = dir->untracked->root;
 -      if (hashcmp(dir->ss_info_exclude.sha1,
 -                  dir->untracked->ss_info_exclude.sha1)) {
 +      if (oidcmp(&dir->ss_info_exclude.oid,
 +                 &dir->untracked->ss_info_exclude.oid)) {
                invalidate_gitignore(dir->untracked, root);
                dir->untracked->ss_info_exclude = dir->ss_info_exclude;
        }
 -      if (hashcmp(dir->ss_excludes_file.sha1,
 -                  dir->untracked->ss_excludes_file.sha1)) {
 +      if (oidcmp(&dir->ss_excludes_file.oid,
 +                 &dir->untracked->ss_excludes_file.oid)) {
                invalidate_gitignore(dir->untracked, root);
                dir->untracked->ss_excludes_file = dir->ss_excludes_file;
        }
@@@ -2261,7 -2248,6 +2261,7 @@@ int read_directory(struct dir_struct *d
                   const char *path, int len, const struct pathspec *pathspec)
  {
        struct untracked_cache_dir *untracked;
 +      uint64_t start = getnanotime();
  
        if (has_symlink_leading_path(path, len))
                return dir->nr;
                dir->nr = i;
        }
  
 +      trace_performance_since(start, "read directory %.*s", len, path);
        if (dir->untracked) {
 +              static int force_untracked_cache = -1;
                static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
 +
 +              if (force_untracked_cache < 0)
 +                      force_untracked_cache =
 +                              git_env_bool("GIT_FORCE_UNTRACKED_CACHE", 0);
                trace_printf_key(&trace_untracked_stats,
                                 "node creation: %u\n"
                                 "gitignore invalidation: %u\n"
                                 dir->untracked->gitignore_invalidated,
                                 dir->untracked->dir_invalidated,
                                 dir->untracked->dir_opened);
 -              if (dir->untracked == istate->untracked &&
 +              if (force_untracked_cache &&
 +                      dir->untracked == istate->untracked &&
                    (dir->untracked->dir_opened ||
                     dir->untracked->gitignore_invalidated ||
                     dir->untracked->dir_invalidated))
@@@ -2659,8 -2638,8 +2659,8 @@@ void write_untracked_extension(struct s
        FLEX_ALLOC_MEM(ouc, exclude_per_dir, untracked->exclude_per_dir, len);
        stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
        stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
 -      hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.sha1);
 -      hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1);
 +      hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.oid.hash);
 +      hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.oid.hash);
        ouc->dir_flags = htonl(untracked->dir_flags);
  
        varint_len = encode_varint(untracked->ident.len, varbuf);
@@@ -2837,12 -2816,13 +2837,12 @@@ static void read_sha1(size_t pos, void 
        rd->data += 20;
  }
  
 -static void load_sha1_stat(struct sha1_stat *sha1_stat,
 -                         const unsigned char *data,
 -                         const unsigned char *sha1)
 +static void load_oid_stat(struct oid_stat *oid_stat, const unsigned char *data,
 +                        const unsigned char *sha1)
  {
 -      stat_data_from_disk(&sha1_stat->stat, data);
 -      hashcpy(sha1_stat->sha1, sha1);
 -      sha1_stat->valid = 1;
 +      stat_data_from_disk(&oid_stat->stat, data);
 +      hashcpy(oid_stat->oid.hash, sha1);
 +      oid_stat->valid = 1;
  }
  
  struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
        uc = xcalloc(1, sizeof(*uc));
        strbuf_init(&uc->ident, ident_len);
        strbuf_add(&uc->ident, ident, ident_len);
 -      load_sha1_stat(&uc->ss_info_exclude,
 -                     next + ouc_offset(info_exclude_stat),
 -                     next + ouc_offset(info_exclude_sha1));
 -      load_sha1_stat(&uc->ss_excludes_file,
 -                     next + ouc_offset(excludes_file_stat),
 -                     next + ouc_offset(excludes_file_sha1));
 +      load_oid_stat(&uc->ss_info_exclude,
 +                    next + ouc_offset(info_exclude_stat),
 +                    next + ouc_offset(info_exclude_sha1));
 +      load_oid_stat(&uc->ss_excludes_file,
 +                    next + ouc_offset(excludes_file_stat),
 +                    next + ouc_offset(excludes_file_sha1));
        uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
        exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
        uc->exclude_per_dir = xstrdup(exclude_per_dir);
@@@ -2988,12 -2968,10 +2988,12 @@@ static int invalidate_one_component(str
  }
  
  void untracked_cache_invalidate_path(struct index_state *istate,
 -                                   const char *path)
 +                                   const char *path, int safe_path)
  {
        if (!istate->untracked || !istate->untracked->root)
                return;
-       if (!safe_path && !verify_path(path))
++      if (!safe_path && !verify_path(path, 0))
 +              return;
        invalidate_one_component(istate->untracked, istate->untracked->root,
                                 path, strlen(path));
  }
  void untracked_cache_remove_from_index(struct index_state *istate,
                                       const char *path)
  {
 -      untracked_cache_invalidate_path(istate, path);
 +      untracked_cache_invalidate_path(istate, path, 1);
  }
  
  void untracked_cache_add_to_index(struct index_state *istate,
                                  const char *path)
  {
 -      untracked_cache_invalidate_path(istate, path);
 +      untracked_cache_invalidate_path(istate, path, 1);
  }
  
  /* Update gitfile and core.worktree setting to connect work tree and git dir */
diff --combined git-compat-util.h
index 07e383257b4985f7400f167d683a5fb692237d93,fc87cbb0904541099a6aa6bd656b70e225778695..76cd42bd637224f4ac95a8164bac9c58f0064301
@@@ -826,8 -826,8 +826,8 @@@ extern ssize_t xpread(int fd, void *buf
  extern int xdup(int fd);
  extern FILE *xfopen(const char *path, const char *mode);
  extern FILE *xfdopen(int fd, const char *mode);
 -extern int xmkstemp(char *template);
 -extern int xmkstemp_mode(char *template, int mode);
 +extern int xmkstemp(char *temp_filename);
 +extern int xmkstemp_mode(char *temp_filename, int mode);
  extern char *xgetcwd(void);
  extern FILE *fopen_for_writing(const char *path);
  extern FILE *fopen_or_warn(const char *path, const char *mode);
@@@ -1001,6 -1001,23 +1001,23 @@@ static inline int sane_iscase(int x, in
                return (x & 0x20) == 0;
  }
  
+ /*
+  * Like skip_prefix, but compare case-insensitively. Note that the comparison
+  * is done via tolower(), so it is strictly ASCII (no multi-byte characters or
+  * locale-specific conversions).
+  */
+ static inline int skip_iprefix(const char *str, const char *prefix,
+                              const char **out)
+ {
+       do {
+               if (!*prefix) {
+                       *out = str;
+                       return 1;
+               }
+       } while (tolower(*str++) == tolower(*prefix++));
+       return 0;
+ }
  static inline int strtoul_ui(char const *s, int base, unsigned int *result)
  {
        unsigned long ul;
diff --combined git-submodule.sh
index 24914963ca23c837e0cc46ca2dc0fa46bf9886a6,f2e5bea715c3b184b98958c763f5ba9917a996ad..7e27d0a0da4d130f9ccb92612848da8e219427a0
@@@ -229,6 -229,11 +229,11 @@@ Use -f if you really want to add it." >
                sm_name="$sm_path"
        fi
  
+       if ! git submodule--helper check-name "$sm_name"
+       then
+               die "$(eval_gettext "'$sm_name' is not a valid submodule name")"
+       fi
        # perhaps the path exists and is already a git repo, else clone it
        if test -e "$sm_path"
        then
@@@ -428,7 -433,60 +433,7 @@@ cmd_deinit(
                shift
        done
  
 -      if test -n "$deinit_all" && test "$#" -ne 0
 -      then
 -              echo >&2 "$(eval_gettext "pathspec and --all are incompatible")"
 -              usage
 -      fi
 -      if test $# = 0 && test -z "$deinit_all"
 -      then
 -              die "$(eval_gettext "Use '--all' if you really want to deinitialize all submodules")"
 -      fi
 -
 -      {
 -              git submodule--helper list --prefix "$wt_prefix" "$@" ||
 -              echo "#unmatched" $?
 -      } |
 -      while read -r mode sha1 stage sm_path
 -      do
 -              die_if_unmatched "$mode" "$sha1"
 -              name=$(git submodule--helper name "$sm_path") || exit
 -
 -              displaypath=$(git submodule--helper relative-path "$sm_path" "$wt_prefix")
 -
 -              # Remove the submodule work tree (unless the user already did it)
 -              if test -d "$sm_path"
 -              then
 -                      # Protect submodules containing a .git directory
 -                      if test -d "$sm_path/.git"
 -                      then
 -                              die "$(eval_gettext "\
 -Submodule work tree '\$displaypath' contains a .git directory
 -(use 'rm -rf' if you really want to remove it including all of its history)")"
 -                      fi
 -
 -                      if test -z "$force"
 -                      then
 -                              git rm -qn "$sm_path" ||
 -                              die "$(eval_gettext "Submodule work tree '\$displaypath' contains local modifications; use '-f' to discard them")"
 -                      fi
 -                      rm -rf "$sm_path" &&
 -                      say "$(eval_gettext "Cleared directory '\$displaypath'")" ||
 -                      say "$(eval_gettext "Could not remove submodule work tree '\$displaypath'")"
 -              fi
 -
 -              mkdir "$sm_path" || say "$(eval_gettext "Could not create empty submodule directory '\$displaypath'")"
 -
 -              # Remove the .git/config entries (unless the user already did it)
 -              if test -n "$(git config --get-regexp submodule."$name\.")"
 -              then
 -                      # Remove the whole section so we have a clean state when
 -                      # the user later decides to init this submodule again
 -                      url=$(git config submodule."$name".url)
 -                      git config --remove-section submodule."$name" 2>/dev/null &&
 -                      say "$(eval_gettext "Submodule '\$name' (\$url) unregistered for path '\$displaypath'")"
 -              fi
 -      done
 +      git ${wt_prefix:+-C "$wt_prefix"} submodule--helper deinit ${GIT_QUIET:+--quiet} ${prefix:+--prefix "$prefix"} ${force:+--force} ${deinit_all:+--all} "$@"
  }
  
  is_tip_reachable () (
@@@ -983,8 -1041,63 +988,8 @@@ cmd_sync(
                        ;;
                esac
        done
 -      cd_to_toplevel
 -      {
 -              git submodule--helper list --prefix "$wt_prefix" "$@" ||
 -              echo "#unmatched" $?
 -      } |
 -      while read -r mode sha1 stage sm_path
 -      do
 -              die_if_unmatched "$mode" "$sha1"
 -
 -              # skip inactive submodules
 -              if ! git submodule--helper is-active "$sm_path"
 -              then
 -                      continue
 -              fi
 -
 -              name=$(git submodule--helper name "$sm_path")
 -              url=$(git config -f .gitmodules --get submodule."$name".url)
 -
 -              # Possibly a url relative to parent
 -              case "$url" in
 -              ./*|../*)
 -                      # rewrite foo/bar as ../.. to find path from
 -                      # submodule work tree to superproject work tree
 -                      up_path="$(printf '%s\n' "$sm_path" | sed "s/[^/][^/]*/../g")" &&
 -                      # guarantee a trailing /
 -                      up_path=${up_path%/}/ &&
 -                      # path from submodule work tree to submodule origin repo
 -                      sub_origin_url=$(git submodule--helper resolve-relative-url "$url" "$up_path") &&
 -                      # path from superproject work tree to submodule origin repo
 -                      super_config_url=$(git submodule--helper resolve-relative-url "$url") || exit
 -                      ;;
 -              *)
 -                      sub_origin_url="$url"
 -                      super_config_url="$url"
 -                      ;;
 -              esac
 -
 -              displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
 -              say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
 -              git config submodule."$name".url "$super_config_url"
 -
 -              if test -e "$sm_path"/.git
 -              then
 -              (
 -                      sanitize_submodule_env
 -                      cd "$sm_path"
 -                      remote=$(get_default_remote)
 -                      git config remote."$remote".url "$sub_origin_url"
  
 -                      if test -n "$recursive"
 -                      then
 -                              prefix="$prefix$sm_path/"
 -                              eval cmd_sync
 -                      fi
 -              )
 -              fi
 -      done
 +      git ${wt_prefix:+-C "$wt_prefix"} ${prefix:+--super-prefix "$prefix"} submodule--helper sync ${GIT_QUIET:+--quiet} ${recursive:+--recursive} "$@"
  }
  
  cmd_absorbgitdirs()
diff --combined read-cache.c
index 59a73f4a81d76a19b8a2280e9643f7c1e715a5d4,0dfc7269dcf485326f2f83b6eb10d17dc28d31f9..4b35e87847f0c346f4968765fa8dc64a863a8047
@@@ -62,7 -62,6 +62,7 @@@ static void replace_index_entry(struct 
        replace_index_entry_in_base(istate, old, ce);
        remove_name_hash(istate, old);
        free(old);
 +      ce->ce_flags &= ~CE_HASHED;
        set_index_entry(istate, nr, ce);
        ce->ce_flags |= CE_UPDATE_IN_BASE;
        mark_fsmonitor_invalid(istate, ce);
  
  void rename_index_entry_at(struct index_state *istate, int nr, const char *new_name)
  {
 -      struct cache_entry *old = istate->cache[nr], *new;
 +      struct cache_entry *old_entry = istate->cache[nr], *new_entry;
        int namelen = strlen(new_name);
  
 -      new = xmalloc(cache_entry_size(namelen));
 -      copy_cache_entry(new, old);
 -      new->ce_flags &= ~CE_HASHED;
 -      new->ce_namelen = namelen;
 -      new->index = 0;
 -      memcpy(new->name, new_name, namelen + 1);
 +      new_entry = xmalloc(cache_entry_size(namelen));
 +      copy_cache_entry(new_entry, old_entry);
 +      new_entry->ce_flags &= ~CE_HASHED;
 +      new_entry->ce_namelen = namelen;
 +      new_entry->index = 0;
 +      memcpy(new_entry->name, new_name, namelen + 1);
  
 -      cache_tree_invalidate_path(istate, old->name);
 -      untracked_cache_remove_from_index(istate, old->name);
 +      cache_tree_invalidate_path(istate, old_entry->name);
 +      untracked_cache_remove_from_index(istate, old_entry->name);
        remove_index_entry_at(istate, nr);
 -      add_index_entry(istate, new, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
 +      add_index_entry(istate, new_entry, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
  }
  
  void fill_stat_data(struct stat_data *sd, struct stat *st)
@@@ -616,26 -615,26 +616,26 @@@ static struct cache_entry *create_alias
                                           struct cache_entry *alias)
  {
        int len;
 -      struct cache_entry *new;
 +      struct cache_entry *new_entry;
  
        if (alias->ce_flags & CE_ADDED)
                die("Will not add file alias '%s' ('%s' already exists in index)", ce->name, alias->name);
  
        /* Ok, create the new entry using the name of the existing alias */
        len = ce_namelen(alias);
 -      new = xcalloc(1, cache_entry_size(len));
 -      memcpy(new->name, alias->name, len);
 -      copy_cache_entry(new, ce);
 +      new_entry = xcalloc(1, cache_entry_size(len));
 +      memcpy(new_entry->name, alias->name, len);
 +      copy_cache_entry(new_entry, ce);
        save_or_free_index_entry(istate, ce);
 -      return new;
 +      return new_entry;
  }
  
  void set_object_name_for_intent_to_add_entry(struct cache_entry *ce)
  {
 -      unsigned char sha1[20];
 -      if (write_sha1_file("", 0, blob_type, sha1))
 +      struct object_id oid;
 +      if (write_object_file("", 0, blob_type, &oid))
                die("cannot create an empty blob in the object database");
 -      hashcpy(ce->oid.hash, sha1);
 +      oidcpy(&ce->oid, &oid);
  }
  
  int add_to_index(struct index_state *istate, const char *path, struct stat *st, int flags)
@@@ -752,7 -751,7 +752,7 @@@ struct cache_entry *make_cache_entry(un
        int size, len;
        struct cache_entry *ce, *ret;
  
-       if (!verify_path(path)) {
+       if (!verify_path(path, mode)) {
                error("Invalid path '%s'", path);
                return NULL;
        }
@@@ -817,7 -816,7 +817,7 @@@ int ce_same_name(const struct cache_ent
   * Also, we don't want double slashes or slashes at the
   * end that can make pathnames ambiguous.
   */
- static int verify_dotfile(const char *rest)
+ static int verify_dotfile(const char *rest, unsigned mode)
  {
        /*
         * The first character was '.', but that
  
        switch (*rest) {
        /*
-        * ".git" followed by  NUL or slash is bad. This
-        * shares the path end test with the ".." case.
+        * ".git" followed by NUL or slash is bad. Note that we match
+        * case-insensitively here, even if ignore_case is not set.
+        * This outlaws ".GIT" everywhere out of an abundance of caution,
+        * since there's really no good reason to allow it.
+        *
+        * Once we've seen ".git", we can also find ".gitmodules", etc (also
+        * case-insensitively).
         */
        case 'g':
        case 'G':
                        break;
                if (rest[2] != 't' && rest[2] != 'T')
                        break;
-               rest += 2;
-       /* fallthrough */
+               if (rest[3] == '\0' || is_dir_sep(rest[3]))
+                       return 0;
+               if (S_ISLNK(mode)) {
+                       rest += 3;
+                       if (skip_iprefix(rest, "modules", &rest) &&
+                           (*rest == '\0' || is_dir_sep(*rest)))
+                               return 0;
+               }
+               break;
        case '.':
                if (rest[1] == '\0' || is_dir_sep(rest[1]))
                        return 0;
        return 1;
  }
  
- int verify_path(const char *path)
+ int verify_path(const char *path, unsigned mode)
  {
        char c;
  
                        return 1;
                if (is_dir_sep(c)) {
  inside:
-                       if (protect_hfs && is_hfs_dotgit(path))
-                               return 0;
-                       if (protect_ntfs && is_ntfs_dotgit(path))
-                               return 0;
+                       if (protect_hfs) {
+                               if (is_hfs_dotgit(path))
+                                       return 0;
+                               if (S_ISLNK(mode)) {
+                                       if (is_hfs_dotgitmodules(path))
+                                               return 0;
+                               }
+                       }
+                       if (protect_ntfs) {
+                               if (is_ntfs_dotgit(path))
+                                       return 0;
+                               if (S_ISLNK(mode)) {
+                                       if (is_ntfs_dotgitmodules(path))
+                                               return 0;
+                               }
+                       }
                        c = *path++;
-                       if ((c == '.' && !verify_dotfile(path)) ||
+                       if ((c == '.' && !verify_dotfile(path, mode)) ||
                            is_dir_sep(c) || c == '\0')
                                return 0;
                }
@@@ -1184,7 -1208,7 +1209,7 @@@ static int add_index_entry_with_check(s
  
        if (!ok_to_add)
                return -1;
-       if (!verify_path(ce->name))
+       if (!verify_path(ce->name, ce->ce_mode))
                return error("Invalid path '%s'", ce->name);
  
        if (!skip_df_check &&
@@@ -1218,8 -1242,9 +1243,8 @@@ int add_index_entry(struct index_state 
        /* Add it in.. */
        istate->cache_nr++;
        if (istate->cache_nr > pos + 1)
 -              memmove(istate->cache + pos + 1,
 -                      istate->cache + pos,
 -                      (istate->cache_nr - pos - 1) * sizeof(ce));
 +              MOVE_ARRAY(istate->cache + pos + 1, istate->cache + pos,
 +                         istate->cache_nr - pos - 1);
        set_index_entry(istate, pos, ce);
        istate->cache_changed |= CE_ENTRY_ADDED;
        return 0;
@@@ -1325,8 -1350,7 +1350,8 @@@ static struct cache_entry *refresh_cach
  
        size = ce_size(ce);
        updated = xmalloc(size);
 -      memcpy(updated, ce, size);
 +      copy_cache_entry(updated, ce);
 +      memcpy(updated->name, ce->name, ce->ce_namelen + 1);
        fill_stat_cache_info(updated, &st);
        /*
         * If ignore_valid is not set, we should leave CE_VALID bit
@@@ -1373,7 -1397,6 +1398,7 @@@ int refresh_index(struct index_state *i
        const char *typechange_fmt;
        const char *added_fmt;
        const char *unmerged_fmt;
 +      uint64_t start = getnanotime();
  
        modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
        deleted_fmt = (in_porcelain ? "D\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");
        for (i = 0; i < istate->cache_nr; i++) {
 -              struct cache_entry *ce, *new;
 +              struct cache_entry *ce, *new_entry;
                int cache_errno = 0;
                int changed = 0;
                int filtered = 0;
                if (filtered)
                        continue;
  
 -              new = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
 -              if (new == ce)
 +              new_entry = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
 +              if (new_entry == ce)
                        continue;
 -              if (!new) {
 +              if (!new_entry) {
                        const char *fmt;
  
                        if (really && cache_errno == EINVAL) {
                        continue;
                }
  
 -              replace_index_entry(istate, i, new);
 +              replace_index_entry(istate, i, new_entry);
        }
 +      trace_performance_since(start, "refresh index");
        return has_errors;
  }
  
@@@ -1548,8 -1570,8 +1573,8 @@@ int verify_ce_order
  
  static int verify_hdr(struct cache_header *hdr, unsigned long size)
  {
 -      git_SHA_CTX c;
 -      unsigned char sha1[20];
 +      git_hash_ctx c;
 +      unsigned char hash[GIT_MAX_RAWSZ];
        int hdr_version;
  
        if (hdr->hdr_signature != htonl(CACHE_SIGNATURE))
        if (!verify_index_checksum)
                return 0;
  
 -      git_SHA1_Init(&c);
 -      git_SHA1_Update(&c, hdr, size - 20);
 -      git_SHA1_Final(sha1, &c);
 -      if (hashcmp(sha1, (unsigned char *)hdr + size - 20))
 +      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))
                return error("bad index file sha1 signature");
        return 0;
  }
@@@ -1794,7 -1816,7 +1819,7 @@@ int do_read_index(struct index_state *i
                die_errno("cannot stat the open index");
  
        mmap_size = xsize_t(st.st_size);
 -      if (mmap_size < sizeof(struct cache_header) + 20)
 +      if (mmap_size < sizeof(struct cache_header) + the_hash_algo->rawsz)
                die("index file smaller than expected");
  
        mmap = xmmap(NULL, mmap_size, PROT_READ, MAP_PRIVATE, fd, 0);
        if (verify_hdr(hdr, mmap_size) < 0)
                goto unmap;
  
 -      hashcpy(istate->sha1, (const unsigned char *)hdr + mmap_size - 20);
 +      hashcpy(istate->sha1, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz);
        istate->version = ntohl(hdr->hdr_version);
        istate->cache_nr = ntohl(hdr->hdr_entries);
        istate->cache_alloc = alloc_nr(istate->cache_nr);
        istate->timestamp.sec = st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
  
 -      while (src_offset <= mmap_size - 20 - 8) {
 +      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
@@@ -1875,7 -1897,6 +1900,7 @@@ 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_sha1_hex;
                return istate->cache_nr;
  
        ret = do_read_index(istate, path, 0);
 +      trace_performance_since(start, "read cache %s", path);
  
        split_index = istate->split_index;
        if (!split_index || is_null_sha1(split_index->base_sha1)) {
        freshen_shared_index(base_path, 0);
        merge_base_index(istate);
        post_read_index_from(istate);
 +      trace_performance_since(start, "read cache %s", base_path);
        free(base_path);
        return ret;
  }
@@@ -1963,11 -1982,11 +1988,11 @@@ int unmerged_index(const struct index_s
  static unsigned char write_buffer[WRITE_BUFFER_SIZE];
  static unsigned long write_buffer_len;
  
 -static int ce_write_flush(git_SHA_CTX *context, int fd)
 +static int ce_write_flush(git_hash_ctx *context, int fd)
  {
        unsigned int buffered = write_buffer_len;
        if (buffered) {
 -              git_SHA1_Update(context, write_buffer, buffered);
 +              the_hash_algo->update_fn(context, write_buffer, buffered);
                if (write_in_full(fd, write_buffer, buffered) < 0)
                        return -1;
                write_buffer_len = 0;
        return 0;
  }
  
 -static int ce_write(git_SHA_CTX *context, int fd, void *data, unsigned int len)
 +static int ce_write(git_hash_ctx *context, int fd, void *data, unsigned int len)
  {
        while (len) {
                unsigned int buffered = write_buffer_len;
        return 0;
  }
  
 -static int write_index_ext_header(git_SHA_CTX *context, int fd,
 +static int write_index_ext_header(git_hash_ctx *context, int fd,
                                  unsigned int ext, unsigned int sz)
  {
        ext = htonl(ext);
                (ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
  }
  
 -static int ce_flush(git_SHA_CTX *context, int fd, unsigned char *sha1)
 +static int ce_flush(git_hash_ctx *context, int fd, unsigned char *hash)
  {
        unsigned int left = write_buffer_len;
  
        if (left) {
                write_buffer_len = 0;
 -              git_SHA1_Update(context, write_buffer, left);
 +              the_hash_algo->update_fn(context, write_buffer, left);
        }
  
 -      /* Flush first if not enough space for SHA1 signature */
 -      if (left + 20 > WRITE_BUFFER_SIZE) {
 +      /* Flush first if not enough space for hash signature */
 +      if (left + the_hash_algo->rawsz > WRITE_BUFFER_SIZE) {
                if (write_in_full(fd, write_buffer, left) < 0)
                        return -1;
                left = 0;
        }
  
 -      /* Append the SHA1 signature at the end */
 -      git_SHA1_Final(write_buffer + left, context);
 -      hashcpy(sha1, write_buffer + left);
 -      left += 20;
 +      /* Append the hash signature at the end */
 +      the_hash_algo->final_fn(write_buffer + left, context);
 +      hashcpy(hash, write_buffer + left);
 +      left += the_hash_algo->rawsz;
        return (write_in_full(fd, write_buffer, left) < 0) ? -1 : 0;
  }
  
@@@ -2106,19 -2125,17 +2131,19 @@@ static void copy_cache_entry_to_ondisk(
        }
  }
  
 -static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
 +static int ce_write_entry(git_hash_ctx *c, int fd, struct cache_entry *ce,
                          struct strbuf *previous_name, struct ondisk_cache_entry *ondisk)
  {
        int size;
 -      int saved_namelen = saved_namelen; /* compiler workaround */
        int result;
 +      unsigned int saved_namelen;
 +      int stripped_name = 0;
        static unsigned char padding[8] = { 0x00 };
  
        if (ce->ce_flags & CE_STRIP_NAME) {
                saved_namelen = ce_namelen(ce);
                ce->ce_namelen = 0;
 +              stripped_name = 1;
        }
  
        if (ce->ce_flags & CE_EXTENDED)
                strbuf_splice(previous_name, common, to_remove,
                              ce->name + common, ce_namelen(ce) - common);
        }
 -      if (ce->ce_flags & CE_STRIP_NAME) {
 +      if (stripped_name) {
                ce->ce_namelen = saved_namelen;
                ce->ce_flags &= ~CE_STRIP_NAME;
        }
@@@ -2175,7 -2192,7 +2200,7 @@@ static int verify_index_from(const stru
        int fd;
        ssize_t n;
        struct stat st;
 -      unsigned char sha1[20];
 +      unsigned char hash[GIT_MAX_RAWSZ];
  
        if (!istate->initialized)
                return 0;
        if (fstat(fd, &st))
                goto out;
  
 -      if (st.st_size < sizeof(struct cache_header) + 20)
 +      if (st.st_size < sizeof(struct cache_header) + the_hash_algo->rawsz)
                goto out;
  
 -      n = pread_in_full(fd, sha1, 20, st.st_size - 20);
 -      if (n != 20)
 +      n = pread_in_full(fd, hash, the_hash_algo->rawsz, st.st_size - the_hash_algo->rawsz);
 +      if (n != the_hash_algo->rawsz)
                goto out;
  
 -      if (hashcmp(istate->sha1, sha1))
 +      if (hashcmp(istate->sha1, hash))
                goto out;
  
        close(fd);
@@@ -2242,9 -2259,8 +2267,9 @@@ void update_index_if_able(struct index_
  static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
                          int strip_extensions)
  {
 +      uint64_t start = getnanotime();
        int newfd = tempfile->fd;
 -      git_SHA_CTX c;
 +      git_hash_ctx c;
        struct cache_header hdr;
        int i, err = 0, removed, extended, hdr_version;
        struct cache_entry **cache = istate->cache;
        hdr.hdr_version = htonl(hdr_version);
        hdr.hdr_entries = htonl(entries - removed);
  
 -      git_SHA1_Init(&c);
 +      the_hash_algo->init_fn(&c);
        if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
                return -1;
  
                return -1;
        istate->timestamp.sec = (unsigned int)st.st_mtime;
        istate->timestamp.nsec = ST_MTIME_NSEC(st);
 +      trace_performance_since(start, "write index, changed mask = %x", istate->cache_changed);
        return 0;
  }
  
@@@ -2542,12 -2557,6 +2567,12 @@@ int write_locked_index(struct index_sta
        int new_shared_index, ret;
        struct split_index *si = istate->split_index;
  
 +      if ((flags & SKIP_IF_UNCHANGED) && !istate->cache_changed) {
 +              if (flags & COMMIT_LOCK)
 +                      rollback_lock_file(lock);
 +              return 0;
 +      }
 +
        if (istate->fsmonitor_last_update)
                fill_fsmonitor_bitmap(istate);