Sync with Git 2.14.4
authorJunio C Hamano <gitster@pobox.com>
Tue, 22 May 2018 05:15:14 +0000 (14:15 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 22 May 2018 05:15:14 +0000 (14:15 +0900)
* maint-2.14:
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
git-compat-util.h
path.c
read-cache.c
submodule-config.c
submodule-config.h
utf8.c
diff --combined apply.c
index d3dc1b292b9ecc38ed6c257a063a86daa7c98d66,d92e58f45362e518bf47e86f587bf7e14468e126..97f387540b17fe233310fa0d17407b6362c82d26
+++ b/apply.c
@@@ -305,33 -305,52 +305,33 @@@ static uint32_t hash_line(const char *c
  static int fuzzy_matchlines(const char *s1, size_t n1,
                            const char *s2, size_t n2)
  {
 -      const char *last1 = s1 + n1 - 1;
 -      const char *last2 = s2 + n2 - 1;
 -      int result = 0;
 +      const char *end1 = s1 + n1;
 +      const char *end2 = s2 + n2;
  
        /* ignore line endings */
 -      while ((*last1 == '\r') || (*last1 == '\n'))
 -              last1--;
 -      while ((*last2 == '\r') || (*last2 == '\n'))
 -              last2--;
 -
 -      /* skip leading whitespaces, if both begin with whitespace */
 -      if (s1 <= last1 && s2 <= last2 && isspace(*s1) && isspace(*s2)) {
 -              while (isspace(*s1) && (s1 <= last1))
 -                      s1++;
 -              while (isspace(*s2) && (s2 <= last2))
 -                      s2++;
 -      }
 -      /* early return if both lines are empty */
 -      if ((s1 > last1) && (s2 > last2))
 -              return 1;
 -      while (!result) {
 -              result = *s1++ - *s2++;
 -              /*
 -               * Skip whitespace inside. We check for whitespace on
 -               * both buffers because we don't want "a b" to match
 -               * "ab"
 -               */
 -              if (isspace(*s1) && isspace(*s2)) {
 -                      while (isspace(*s1) && s1 <= last1)
 +      while (s1 < end1 && (end1[-1] == '\r' || end1[-1] == '\n'))
 +              end1--;
 +      while (s2 < end2 && (end2[-1] == '\r' || end2[-1] == '\n'))
 +              end2--;
 +
 +      while (s1 < end1 && s2 < end2) {
 +              if (isspace(*s1)) {
 +                      /*
 +                       * Skip whitespace. We check on both buffers
 +                       * because we don't want "a b" to match "ab".
 +                       */
 +                      if (!isspace(*s2))
 +                              return 0;
 +                      while (s1 < end1 && isspace(*s1))
                                s1++;
 -                      while (isspace(*s2) && s2 <= last2)
 +                      while (s2 < end2 && isspace(*s2))
                                s2++;
 -              }
 -              /*
 -               * If we reached the end on one side only,
 -               * lines don't match
 -               */
 -              if (
 -                  ((s2 > last2) && (s1 <= last1)) ||
 -                  ((s1 > last1) && (s2 <= last2)))
 +              } else if (*s1++ != *s2++)
                        return 0;
 -              if ((s1 > last1) && (s2 > last2))
 -                      break;
        }
  
 -      return !result;
 +      /* If we reached the end on one side only, lines don't match. */
 +      return s1 == end1 && s2 == end2;
  }
  
  static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
@@@ -793,13 -812,16 +793,13 @@@ static int has_epoch_timestamp(const ch
         * 1970-01-01, and the seconds part must be "00".
         */
        const char stamp_regexp[] =
 -              "^(1969-12-31|1970-01-01)"
 -              " "
 -              "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
 +              "^[0-2][0-9]:([0-5][0-9]):00(\\.0+)?"
                " "
                "([-+][0-2][0-9]:?[0-5][0-9])\n";
        const char *timestamp = NULL, *cp, *colon;
        static regex_t *stamp;
        regmatch_t m[10];
 -      int zoneoffset;
 -      int hourminute;
 +      int zoneoffset, epoch_hour, hour, minute;
        int status;
  
        for (cp = nameline; *cp != '\n'; cp++) {
        }
        if (!timestamp)
                return 0;
 +
 +      /*
 +       * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
 +       * (west of GMT) or 1970-01-01 (east of GMT)
 +       */
 +      if (skip_prefix(timestamp, "1969-12-31 ", &timestamp))
 +              epoch_hour = 24;
 +      else if (skip_prefix(timestamp, "1970-01-01 ", &timestamp))
 +              epoch_hour = 0;
 +      else
 +              return 0;
 +
        if (!stamp) {
                stamp = xmalloc(sizeof(*stamp));
                if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
                return 0;
        }
  
 +      hour = strtol(timestamp, NULL, 10);
 +      minute = strtol(timestamp + m[1].rm_so, NULL, 10);
 +
        zoneoffset = strtol(timestamp + m[3].rm_so + 1, (char **) &colon, 10);
        if (*colon == ':')
                zoneoffset = zoneoffset * 60 + strtol(colon + 1, NULL, 10);
        if (timestamp[m[3].rm_so] == '-')
                zoneoffset = -zoneoffset;
  
 -      /*
 -       * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
 -       * (west of GMT) or 1970-01-01 (east of GMT)
 -       */
 -      if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) ||
 -          (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10)))
 -              return 0;
 -
 -      hourminute = (strtol(timestamp + 11, NULL, 10) * 60 +
 -                    strtol(timestamp + 14, NULL, 10) -
 -                    zoneoffset);
 -
 -      return ((zoneoffset < 0 && hourminute == 1440) ||
 -              (0 <= zoneoffset && !hourminute));
 +      return hour * 60 + minute - zoneoffset == epoch_hour * 60;
  }
  
  /*
@@@ -2901,7 -2921,6 +2901,7 @@@ static int apply_one_fragment(struct ap
                        if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
                            ws_blank_line(patch + 1, plen, ws_rule))
                                is_blank_context = 1;
 +                      /* fallthrough */
                case '-':
                        memcpy(old, patch + 1, plen);
                        add_line_info(&preimage, old, plen,
                        old += plen;
                        if (first == '-')
                                break;
 -              /* Fall-through for ' ' */
 +                      /* fallthrough */
                case '+':
                        /* --no-add does not add new lines */
                        if (first == '+' && state->no_add)
            newlines.len > 0 && newlines.buf[newlines.len - 1] == '\n') {
                old--;
                strbuf_setlen(&newlines, newlines.len - 1);
 +              preimage.line_allocated[preimage.nr - 1].len--;
 +              postimage.line_allocated[postimage.nr - 1].len--;
        }
  
        leading = frag->leading;
@@@ -3560,7 -3577,7 +3560,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);
 -      else if (get_sha1(patch->old_sha1_prefix, pre_oid.hash) ||
 +      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."));
  
@@@ -3865,9 -3882,9 +3865,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;
  }
@@@ -4084,7 -4101,7 +4084,7 @@@ static int build_fake_ancestor(struct a
                        else
                                return error(_("sha1 information is lacking or "
                                               "useless for submodule %s"), name);
 -              } else if (!get_sha1_blob(patch->old_sha1_prefix, oid.hash)) {
 +              } else if (!get_oid_blob(patch->old_sha1_prefix, &oid)) {
                        ; /* ok */
                } else if (!patch->lines_added && !patch->lines_deleted) {
                        /* mode-only change: update the current */
index 06ed02f99402be6a7edb36ff0eb08b903c513c6c,e8ccddd3b1b4473a23a5b50434a140f526f5ca7a..77940499b0c0670798a19548c023f7c27ddfd327
@@@ -17,8 -17,9 +17,8 @@@
  static char *get_default_remote(void)
  {
        char *dest = NULL, *ret;
 -      unsigned char sha1[20];
        struct strbuf sb = STRBUF_INIT;
 -      const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
 +      const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
  
        if (!refname)
                die(_("No such ref: %s"), "HEAD");
@@@ -274,6 -275,8 +274,6 @@@ static void module_list_active(struct m
        int i;
        struct module_list active_modules = MODULE_LIST_INIT;
  
 -      gitmodules_config();
 -
        for (i = 0; i < list->nr; i++) {
                const struct cache_entry *ce = list->entries[i];
  
@@@ -334,6 -337,9 +334,6 @@@ static void init_submodule(const char *
        struct strbuf sb = STRBUF_INIT;
        char *upd = NULL, *url = NULL, *displaypath;
  
 -      /* Only loads from .gitmodules, no overlay with .git/config */
 -      gitmodules_config();
 -
        if (prefix && get_super_prefix())
                die("BUG: cannot have prefix and superprefix");
        else if (prefix)
        } else
                displaypath = xstrdup(path);
  
 -      sub = submodule_from_path(null_sha1, path);
 +      sub = submodule_from_path(&null_oid, path);
  
        if (!sub)
                die(_("No url found for submodule path '%s' in .gitmodules"),
@@@ -469,7 -475,8 +469,7 @@@ static int module_name(int argc, const 
        if (argc != 2)
                usage(_("git submodule--helper name <path>"));
  
 -      gitmodules_config();
 -      sub = submodule_from_path(null_sha1, argv[1]);
 +      sub = submodule_from_path(&null_oid, argv[1]);
  
        if (!sub)
                die(_("no submodule mapping found in .gitmodules for path '%s'"),
@@@ -773,10 -780,6 +773,10 @@@ static int prepare_to_clone_next_submod
                                           struct strbuf *out)
  {
        const struct submodule *sub = NULL;
 +      const char *url = NULL;
 +      const char *update_string;
 +      enum submodule_update_type update_type;
 +      char *key;
        struct strbuf displaypath_sb = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
        const char *displaypath = NULL;
                goto cleanup;
        }
  
 -      sub = submodule_from_path(null_sha1, ce->name);
 +      sub = submodule_from_path(&null_oid, ce->name);
  
        if (suc->recursive_prefix)
                displaypath = relative_path(suc->recursive_prefix,
                goto cleanup;
        }
  
 +      key = xstrfmt("submodule.%s.update", sub->name);
 +      if (!repo_config_get_string_const(the_repository, key, &update_string)) {
 +              update_type = parse_submodule_update_type(update_string);
 +      } else {
 +              update_type = sub->update_strategy.type;
 +      }
 +      free(key);
 +
        if (suc->update.type == SM_UPDATE_NONE
            || (suc->update.type == SM_UPDATE_UNSPECIFIED
 -              && sub->update_strategy.type == SM_UPDATE_NONE)) {
 +              && update_type == SM_UPDATE_NONE)) {
                strbuf_addf(out, _("Skipping submodule '%s'"), displaypath);
                strbuf_addch(out, '\n');
                goto cleanup;
                goto cleanup;
        }
  
 +      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;
 +
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/.git", ce->name);
        needs_cloning = !file_exists(sb.buf);
                argv_array_push(&child->args, "--depth=1");
        argv_array_pushl(&child->args, "--path", sub->path, NULL);
        argv_array_pushl(&child->args, "--name", sub->name, NULL);
 -      argv_array_pushl(&child->args, "--url", sub->url, NULL);
 +      argv_array_pushl(&child->args, "--url", url, NULL);
        if (suc->references.nr) {
                struct string_list_item *item;
                for_each_string_list_item(item, &suc->references)
@@@ -970,19 -960,10 +970,19 @@@ static int update_clone_task_finished(i
        return 0;
  }
  
 +static int gitmodules_update_clone_config(const char *var, const char *value,
 +                                        void *cb)
 +{
 +      int *max_jobs = cb;
 +      if (!strcmp(var, "submodule.fetchjobs"))
 +              *max_jobs = parse_submodule_fetchjobs(var, value);
 +      return 0;
 +}
 +
  static int update_clone(int argc, const char **argv, const char *prefix)
  {
        const char *update = NULL;
 -      int max_jobs = -1;
 +      int max_jobs = 1;
        struct string_list_item *item;
        struct pathspec pathspec;
        struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
        };
        suc.prefix = prefix;
  
 +      config_from_gitmodules(gitmodules_update_clone_config, &max_jobs);
 +      git_config(gitmodules_update_clone_config, &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;
  
 -      /* Overlay the parsed .gitmodules file with .git/config */
 -      gitmodules_config();
 -      git_config(submodule_config, NULL);
 -
 -      if (max_jobs < 0)
 -              max_jobs = parallel_submodules();
 -
        run_processes_parallel(max_jobs,
                               update_clone_get_next_task,
                               update_clone_start_failure,
@@@ -1072,23 -1057,19 +1072,23 @@@ static int resolve_relative_path(int ar
  static const char *remote_submodule_branch(const char *path)
  {
        const struct submodule *sub;
 -      gitmodules_config();
 -      git_config(submodule_config, NULL);
 +      const char *branch = NULL;
 +      char *key;
  
 -      sub = submodule_from_path(null_sha1, path);
 +      sub = submodule_from_path(&null_oid, path);
        if (!sub)
                return NULL;
  
 -      if (!sub->branch)
 +      key = xstrfmt("submodule.%s.branch", sub->name);
 +      if (repo_config_get_string_const(the_repository, key, &branch))
 +              branch = sub->branch;
 +      free(key);
 +
 +      if (!branch)
                return "master";
  
 -      if (!strcmp(sub->branch, ".")) {
 -              unsigned char sha1[20];
 -              const char *refname = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
 +      if (!strcmp(branch, ".")) {
 +              const char *refname = resolve_ref_unsafe("HEAD", 0, NULL, NULL);
  
                if (!refname)
                        die(_("No such ref: %s"), "HEAD");
                return refname;
        }
  
 -      return sub->branch;
 +      return branch;
  }
  
  static int resolve_remote_submodule_branch(int argc, const char **argv,
@@@ -1187,7 -1168,6 +1187,7 @@@ static int push_check(int argc, const c
                                                break;
                                        die("HEAD does not match the named branch in the superproject");
                                }
 +                              /* fallthrough */
                        default:
                                die("src refspec '%s' must name a ref",
                                    rs->src);
@@@ -1224,6 -1204,9 +1224,6 @@@ static int absorb_git_dirs(int argc, co
        argc = parse_options(argc, argv, prefix, embed_gitdir_options,
                             git_submodule_helper_usage, 0);
  
 -      gitmodules_config();
 -      git_config(submodule_config, NULL);
 -
        if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
                return 1;
  
@@@ -1239,9 -1222,34 +1239,32 @@@ static int is_active(int argc, const ch
        if (argc != 2)
                die("submodule--helper is-active takes exactly 1 argument");
  
 -      gitmodules_config();
 -
        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 {
@@@ -1263,6 -1271,7 +1286,7 @@@ static struct cmd_struct commands[] = 
        {"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 bf7420b808fd1e0314d981bf65073df62f7578a4,dc11661119bffd7fb40cd45741533af31f62268a..a9fde000d4771acb2c329a991377d58578098fc1
@@@ -280,17 -280,15 +280,17 @@@ static int add_one_path(const struct ca
        fill_stat_cache_info(ce, st);
        ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
  
 -      if (index_path(ce->oid.hash, path, st,
 +      if (index_path(&ce->oid, path, st,
                       info_only ? 0 : HASH_WRITE_OBJECT)) {
                free(ce);
                return -1;
        }
        option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
        option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
 -      if (add_cache_entry(ce, option))
 +      if (add_cache_entry(ce, option)) {
 +              free(ce);
                return error("%s: cannot add to the index - missing --add option?", path);
 +      }
        return 0;
  }
  
@@@ -361,10 -359,9 +361,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);
@@@ -446,7 -443,17 +445,17 @@@ 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)
+               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);
  }
@@@ -537,7 -544,7 +546,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;
                }
@@@ -917,7 -924,7 +926,7 @@@ int cmd_update_index(int argc, const ch
        struct refresh_params refresh_args = {0, &has_errors};
        int lock_error = 0;
        int split_index = -1;
 -      struct lock_file *lock_file;
 +      struct lock_file lock_file = LOCK_INIT;
        struct parse_opt_ctx_t ctx;
        strbuf_getline_fn getline_fn;
        int parseopt_state = PARSE_OPT_UNKNOWN;
  
        git_config(git_default_config, NULL);
  
 -      /* We can't free this memory, it becomes part of a linked list parsed atexit() */
 -      lock_file = xcalloc(1, sizeof(struct lock_file));
 -
        /* we will diagnose later if it turns out that we need to update it */
 -      newfd = hold_locked_index(lock_file, 0);
 +      newfd = hold_locked_index(&lock_file, 0);
        if (newfd < 0)
                lock_error = errno;
  
                                exit(128);
                        unable_to_lock_die(get_index_file(), lock_error);
                }
 -              if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
 +              if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
                        die("Unable to write new index file");
        }
  
 -      rollback_lock_file(lock_file);
 +      rollback_lock_file(&lock_file);
  
        return has_errors ? 1 : 0;
  }
diff --combined cache.h
index 6440e2bf21f5800db98f82fcedff46478188674e,ffadd5bc7082fa2197d0ce63bb224a635c566de7..803bd7094a414eee9931746850011aae062b8976
+++ b/cache.h
@@@ -4,7 -4,6 +4,7 @@@
  #include "git-compat-util.h"
  #include "strbuf.h"
  #include "hashmap.h"
 +#include "mru.h"
  #include "advice.h"
  #include "gettext.h"
  #include "convert.h"
@@@ -418,6 -417,7 +418,6 @@@ static inline enum object_type object_t
  #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
  #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
  #define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
 -#define GIT_TOPLEVEL_PREFIX_ENVIRONMENT "GIT_INTERNAL_TOPLEVEL_PREFIX"
  #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
  #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
  #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
  #define GITATTRIBUTES_FILE ".gitattributes"
  #define INFOATTRIBUTES_FILE "info/attributes"
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
 +#define GITMODULES_FILE ".gitmodules"
  #define GIT_NOTES_REF_ENVIRONMENT "GIT_NOTES_REF"
  #define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
  #define GIT_NOTES_DISPLAY_REF_ENVIRONMENT "GIT_NOTES_DISPLAY_REF"
  #define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
  #define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
  #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
 +#define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
  
  /*
   * This environment variable is expected to contain a boolean indicating
@@@ -608,7 -606,7 +608,7 @@@ extern int write_locked_index(struct in
  extern int discard_index(struct index_state *);
  extern void move_index_extensions(struct index_state *dst, struct index_state *src);
  extern int unmerged_index(const struct index_state *);
- 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);
@@@ -686,8 -684,8 +686,8 @@@ extern int ie_modified(const struct ind
  
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
 -extern int index_fd(unsigned char *sha1, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 -extern int index_path(unsigned char *sha1, const char *path, struct stat *st, unsigned flags);
 +extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 +extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
  
  /*
   * Record to sd the data from st that we use to check whether a file
@@@ -784,11 -782,6 +784,11 @@@ extern int protect_ntfs
   */
  extern int ref_paranoia;
  
 +/*
 + * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
 + */
 +int use_optional_locks(void);
 +
  /*
   * The character that begins a commented line in user-editable file
   * that is subject to stripspace.
@@@ -909,6 -902,20 +909,6 @@@ extern void check_repository_format(voi
   */
  extern const char *sha1_file_name(const unsigned char *sha1);
  
 -/*
 - * Return the name of the (local) packfile with the specified sha1 in
 - * its name.  The return value is a pointer to memory that is
 - * overwritten each time this function is called.
 - */
 -extern char *sha1_pack_name(const unsigned char *sha1);
 -
 -/*
 - * Return the name of the (local) pack index file with the specified
 - * sha1 in its name.  The return value is a pointer to memory that is
 - * overwritten each time this function is called.
 - */
 -extern char *sha1_pack_index_name(const unsigned char *sha1);
 -
  /*
   * Return an abbreviated sha1 unique within this repository's object database.
   * The result will be at least `len` characters long, and will be NUL
@@@ -1130,7 -1137,15 +1130,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
@@@ -1185,7 -1200,7 +1193,7 @@@ static inline const unsigned char *look
  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, unsigned char *sha1, unsigned flags);
 +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 git_open_cloexec(const char *name, int flags);
@@@ -1194,10 -1209,15 +1202,10 @@@ extern void *map_sha1_file(const unsign
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
  extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
  
 -/* global flag to enable extra checks when accessing packed objects */
 -extern int do_check_packed_object_crc;
 -
  extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
  
  extern int finalize_object_file(const char *tmpfile, const char *filename);
  
 -extern int has_sha1_pack(const unsigned char *sha1);
 -
  /*
   * Open the loose object at path, check its sha1, and return the contents,
   * type, and size. If the object is a blob, then "contents" may return NULL,
@@@ -1233,6 -1253,8 +1241,6 @@@ extern int has_object_file_with_flags(c
   */
  extern int has_loose_object_nonlocal(const unsigned char *sha1);
  
 -extern int has_pack_index(const unsigned char *sha1);
 -
  extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
  
  /* Helper to check and "touch" a file */
@@@ -1270,37 -1292,38 +1278,37 @@@ struct object_context 
         */
        struct strbuf symlink_path;
        /*
 -       * If GET_SHA1_RECORD_PATH is set, this will record path (if any)
 +       * If GET_OID_RECORD_PATH is set, this will record path (if any)
         * found when resolving the name. The caller is responsible for
         * releasing the memory.
         */
        char *path;
  };
  
 -#define GET_SHA1_QUIETLY           01
 -#define GET_SHA1_COMMIT            02
 -#define GET_SHA1_COMMITTISH        04
 -#define GET_SHA1_TREE             010
 -#define GET_SHA1_TREEISH          020
 -#define GET_SHA1_BLOB             040
 -#define GET_SHA1_FOLLOW_SYMLINKS 0100
 -#define GET_SHA1_RECORD_PATH     0200
 -#define GET_SHA1_ONLY_TO_DIE    04000
 -
 -#define GET_SHA1_DISAMBIGUATORS \
 -      (GET_SHA1_COMMIT | GET_SHA1_COMMITTISH | \
 -      GET_SHA1_TREE | GET_SHA1_TREEISH | \
 -      GET_SHA1_BLOB)
 -
 -extern int get_sha1(const char *str, unsigned char *sha1);
 -extern int get_sha1_commit(const char *str, unsigned char *sha1);
 -extern int get_sha1_committish(const char *str, unsigned char *sha1);
 -extern int get_sha1_tree(const char *str, unsigned char *sha1);
 -extern int get_sha1_treeish(const char *str, unsigned char *sha1);
 -extern int get_sha1_blob(const char *str, unsigned char *sha1);
 -extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
 -extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc);
 +#define GET_OID_QUIETLY           01
 +#define GET_OID_COMMIT            02
 +#define GET_OID_COMMITTISH        04
 +#define GET_OID_TREE             010
 +#define GET_OID_TREEISH          020
 +#define GET_OID_BLOB             040
 +#define GET_OID_FOLLOW_SYMLINKS 0100
 +#define GET_OID_RECORD_PATH     0200
 +#define GET_OID_ONLY_TO_DIE    04000
 +
 +#define GET_OID_DISAMBIGUATORS \
 +      (GET_OID_COMMIT | GET_OID_COMMITTISH | \
 +      GET_OID_TREE | GET_OID_TREEISH | \
 +      GET_OID_BLOB)
  
  extern int get_oid(const char *str, struct object_id *oid);
 +extern int get_oid_commit(const char *str, struct object_id *oid);
 +extern int get_oid_committish(const char *str, struct object_id *oid);
 +extern int get_oid_tree(const char *str, struct object_id *oid);
 +extern int get_oid_treeish(const char *str, struct object_id *oid);
 +extern int get_oid_blob(const char *str, struct object_id *oid);
 +extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
 +extern int get_oid_with_context(const char *str, unsigned flags, struct object_id *oid, struct object_context *oc);
 +
  
  typedef int each_abbrev_fn(const struct object_id *oid, void *);
  extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
@@@ -1596,7 -1619,8 +1604,7 @@@ extern struct 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).
   */
 -struct mru;
 -extern struct mru *packed_git_mru;
 +extern struct mru packed_git_mru;
  
  struct pack_entry {
        off_t offset;
        struct packed_git *p;
  };
  
 -extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 -
 -/* A hook to report invalid files in pack directory */
 -#define PACKDIR_FILE_PACK 1
 -#define PACKDIR_FILE_IDX 2
 -#define PACKDIR_FILE_GARBAGE 4
 -extern void (*report_garbage)(unsigned seen_bits, const char *path);
 -
 -extern void prepare_packed_git(void);
 -extern void reprepare_packed_git(void);
 -extern void install_packed_git(struct packed_git *pack);
 -
 -/*
 - * Give a rough count of objects in the repository. This sacrifices accuracy
 - * for speed.
 - */
 -unsigned long approximate_object_count(void);
 -
 -extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 -                                       struct packed_git *packs);
 -
 -extern void pack_report(void);
 -
  /*
   * Create a temporary file rooted in the object database directory, or
   * die on failure. The filename is taken from "pattern", which should have the
   */
  extern int odb_mkstemp(struct strbuf *template, const char *pattern);
  
 -/*
 - * Generate the filename to be used for a pack file with checksum "sha1" and
 - * extension "ext". The result is written into the strbuf "buf", overwriting
 - * any existing contents. A pointer to buf->buf is returned as a convenience.
 - *
 - * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
 - */
 -extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
 -
  /*
   * Create a pack .keep file named "name" (which should generally be the output
   * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
   */
  extern int odb_pack_keep(const char *name);
  
 -/*
 - * mmap the index file for the specified packfile (if it is not
 - * already mmapped).  Return 0 on success.
 - */
 -extern int open_pack_index(struct packed_git *);
 -
 -/*
 - * munmap the index file for the specified packfile (if it is
 - * currently mmapped).
 - */
 -extern void close_pack_index(struct packed_git *);
 -
 -extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 -extern void close_pack_windows(struct packed_git *);
 -extern void close_all_packs(void);
 -extern void unuse_pack(struct pack_window **);
 -extern void clear_delta_base_cache(void);
 -extern struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
 -
 -/*
 - * Make sure that a pointer access into an mmap'd index file is within bounds,
 - * and can provide at least 8 bytes of data.
 - *
 - * Note that this is only necessary for variable-length segments of the file
 - * (like the 64-bit extended offset table), as we compare the size to the
 - * fixed-length parts when we open the file.
 - */
 -extern void check_pack_index_ptr(const struct packed_git *p, const void *ptr);
 -
 -/*
 - * Return the SHA-1 of the nth object within the specified packfile.
 - * Open the index if it is not already open.  The return value points
 - * at the SHA-1 within the mmapped index.  Return NULL if there is an
 - * error.
 - */
 -extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
 -/*
 - * Like nth_packed_object_sha1, but write the data into the object specified by
 - * the the first argument.  Returns the first argument on success, and NULL on
 - * error.
 - */
 -extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
 -
 -/*
 - * Return the offset of the nth object within the specified packfile.
 - * The index must already be opened.
 - */
 -extern off_t nth_packed_object_offset(const struct packed_git *, uint32_t n);
 -
 -/*
 - * If the object named sha1 is present in the specified packfile,
 - * return its offset within the packfile; otherwise, return 0.
 - */
 -extern off_t find_pack_entry_one(const unsigned char *sha1, struct packed_git *);
 -
 -extern int is_pack_valid(struct packed_git *);
 -extern void *unpack_entry(struct packed_git *, off_t, enum object_type *, unsigned long *);
 -extern unsigned long unpack_object_header_buffer(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
 -extern unsigned long get_size_from_delta(struct packed_git *, struct pack_window **, off_t);
 -extern int unpack_object_header(struct packed_git *, struct pack_window **, off_t *, unsigned long *);
 -
  /*
   * Iterate over the files in the loose-object parts of the object
   * directory "path", triggering the following callbacks:
@@@ -1668,12 -1785,17 +1676,12 @@@ int for_each_loose_file_in_objdir_buf(s
                                      void *data);
  
  /*
 - * Iterate over loose and packed objects in both the local
 + * Iterate over loose objects in both the local
   * repository and any alternates repositories (unless the
   * LOCAL_ONLY flag is set).
   */
  #define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
 -typedef int each_packed_object_fn(const struct object_id *oid,
 -                                struct packed_git *pack,
 -                                uint32_t pos,
 -                                void *data);
  extern int for_each_loose_object(each_loose_object_fn, void *, unsigned flags);
 -extern int for_each_packed_object(each_packed_object_fn, void *, unsigned flags);
  
  struct object_info {
        /* Request */
  /* Do not retry packed storage after checking packed and loose storage */
  #define OBJECT_INFO_QUICK 8
  extern int sha1_object_info_extended(const unsigned char *, struct object_info *, unsigned flags);
 -extern int packed_object_info(struct packed_git *pack, off_t offset, struct object_info *);
  
  /* Dumb servers support */
  extern int update_server_info(int);
@@@ -1841,8 -1964,6 +1849,8 @@@ void shift_tree_by(const struct object_
  #define WS_TRAILING_SPACE      (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
  #define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)
  #define WS_TAB_WIDTH_MASK        077
 +/* All WS_* -- when extended, adapt diff.c emit_symbol */
 +#define WS_RULE_MASK           07777
  extern unsigned whitespace_rule_cfg;
  extern unsigned whitespace_rule(const char *);
  extern unsigned parse_whitespace_rule(const char *);
diff --combined git-compat-util.h
index cedad4d5818a6e5c6e1732d34cf8fef6dab36718,6cb3c2f19eb5a0822ae17a1b1e1abc08d3de25e4..0d270ffc2788403ac29169f3cba2ae925a4ef0e3
@@@ -749,6 -749,8 +749,6 @@@ const char *inet_ntop(int af, const voi
  extern int git_atexit(void (*handler)(void));
  #endif
  
 -extern void release_pack_memory(size_t);
 -
  typedef void (*try_to_free_t)(size_t);
  extern try_to_free_t set_try_to_free_routine(try_to_free_t);
  
@@@ -978,6 -980,23 +978,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;
@@@ -1171,24 -1190,4 +1188,24 @@@ static inline int is_missing_file_error
  
  extern int cmd_main(int, const char **);
  
 +/*
 + * You can mark a stack variable with UNLEAK(var) to avoid it being
 + * reported as a leak by tools like LSAN or valgrind. The argument
 + * should generally be the variable itself (not its address and not what
 + * it points to). It's safe to use this on pointers which may already
 + * have been freed, or on pointers which may still be in use.
 + *
 + * Use this _only_ for a variable that leaks by going out of scope at
 + * program exit (so only from cmd_* functions or their direct helpers).
 + * Normal functions, especially those which may be called multiple
 + * times, should actually free their memory. This is only meant as
 + * an annotation, and does nothing in non-leak-checking builds.
 + */
 +#ifdef SUPPRESS_ANNOTATED_LEAKS
 +extern void unleak_memory(const void *ptr, size_t len);
 +#define UNLEAK(var) unleak_memory(&(var), sizeof(var))
 +#else
 +#define UNLEAK(var) do {} while (0)
 +#endif
 +
  #endif
diff --combined path.c
index da8b655730d363dda5010bdf2d53bd76abb82931,9ac0531a29b94e7d83c52c172d566a0e243e0025..4c4a751539aa30d134da09dd6ad1f00988818e88
--- 1/path.c
--- 2/path.c
+++ b/path.c
@@@ -9,7 -9,6 +9,7 @@@
  #include "worktree.h"
  #include "submodule-config.h"
  #include "path.h"
 +#include "packfile.h"
  
  static int get_st_mode_bits(const char *path, int *mode)
  {
@@@ -192,7 -191,7 +192,7 @@@ static void *add_to_trie(struct trie *r
                 * Split this node: child will contain this node's
                 * existing children.
                 */
 -              child = malloc(sizeof(*child));
 +              child = xmalloc(sizeof(*child));
                memcpy(child->children, root->children, sizeof(root->children));
  
                child->len = root->len - i - 1;
@@@ -717,7 -716,7 +717,7 @@@ char *expand_user_path(const char *path
                        if (!home)
                                goto return_null;
                        if (real_home)
 -                              strbuf_addstr(&user_path, real_path(home));
 +                              strbuf_add_real_path(&user_path, home);
                        else
                                strbuf_addstr(&user_path, home);
  #ifdef GIT_WINDOWS_NATIVE
@@@ -1305,7 -1304,7 +1305,7 @@@ static int only_spaces_and_periods(cons
  
  int is_ntfs_dotgit(const char *name)
  {
-       int len;
+       size_t len;
  
        for (len = 0; ; len++)
                if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
                }
  }
  
+ static int is_ntfs_dot_generic(const char *name,
+                              const char *dotgit_name,
+                              size_t len,
+                              const char *dotgit_ntfs_shortname_prefix)
+ {
+       int saw_tilde;
+       size_t i;
+       if ((name[0] == '.' && !strncasecmp(name + 1, dotgit_name, len))) {
+               i = len + 1;
+ only_spaces_and_periods:
+               for (;;) {
+                       char c = name[i++];
+                       if (!c)
+                               return 1;
+                       if (c != ' ' && c != '.')
+                               return 0;
+               }
+       }
+       /*
+        * Is it a regular NTFS short name, i.e. shortened to 6 characters,
+        * followed by ~1, ... ~4?
+        */
+       if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' &&
+           name[7] >= '1' && name[7] <= '4') {
+               i = 8;
+               goto only_spaces_and_periods;
+       }
+       /*
+        * Is it a fall-back NTFS short name (for details, see
+        * https://en.wikipedia.org/wiki/8.3_filename?
+        */
+       for (i = 0, saw_tilde = 0; i < 8; i++)
+               if (name[i] == '\0')
+                       return 0;
+               else if (saw_tilde) {
+                       if (name[i] < '0' || name[i] > '9')
+                               return 0;
+               } else if (name[i] == '~') {
+                       if (name[++i] < '1' || name[i] > '9')
+                               return 0;
+                       saw_tilde = 1;
+               } else if (i >= 6)
+                       return 0;
+               else if (name[i] < 0) {
+                       /*
+                        * We know our needles contain only ASCII, so we clamp
+                        * here to make the results of tolower() sane.
+                        */
+                       return 0;
+               } else if (tolower(name[i]) != dotgit_ntfs_shortname_prefix[i])
+                       return 0;
+       goto only_spaces_and_periods;
+ }
+ /*
+  * Inline helper to make sure compiler resolves strlen() on literals at
+  * compile time.
+  */
+ static inline int is_ntfs_dot_str(const char *name, const char *dotgit_name,
+                                 const char *dotgit_ntfs_shortname_prefix)
+ {
+       return is_ntfs_dot_generic(name, dotgit_name, strlen(dotgit_name),
+                                  dotgit_ntfs_shortname_prefix);
+ }
+ int is_ntfs_dotgitmodules(const char *name)
+ {
+       return is_ntfs_dot_str(name, "gitmodules", "gi7eba");
+ }
+ int is_ntfs_dotgitignore(const char *name)
+ {
+       return is_ntfs_dot_str(name, "gitignore", "gi250a");
+ }
+ int is_ntfs_dotgitattributes(const char *name)
+ {
+       return is_ntfs_dot_str(name, "gitattributes", "gi7d29");
+ }
  int looks_like_command_line_option(const char *str)
  {
        return str && str[0] == '-';
diff --combined read-cache.c
index 65f4fe8375d59234d5e58f87a8593fc50e6336a2,5b57b369e86b88c992d3158eca43c50a0b01d4c9..8cef7b671548e5fdc3aa1daa1bff02e3c4efdb47
@@@ -160,9 -160,9 +160,9 @@@ static int ce_compare_data(const struc
        int fd = git_open_cloexec(ce->name, O_RDONLY);
  
        if (fd >= 0) {
 -              unsigned char sha1[20];
 -              if (!index_fd(sha1, fd, st, OBJ_BLOB, ce->name, 0))
 -                      match = hashcmp(sha1, ce->oid.hash);
 +              struct object_id oid;
 +              if (!index_fd(&oid, fd, st, OBJ_BLOB, ce->name, 0))
 +                      match = oidcmp(&oid, &ce->oid);
                /* index_fd() closed the file descriptor already */
        }
        return match;
@@@ -220,7 -220,6 +220,7 @@@ static int ce_modified_check_fs(const s
        case S_IFDIR:
                if (S_ISGITLINK(ce->ce_mode))
                        return ce_compare_gitlink(ce) ? DATA_CHANGED : 0;
 +              /* else fallthrough */
        default:
                return TYPE_CHANGED;
        }
@@@ -690,7 -689,7 +690,7 @@@ int add_to_index(struct index_state *is
                return 0;
        }
        if (!intent_only) {
 -              if (index_path(ce->oid.hash, path, st, HASH_WRITE_OBJECT)) {
 +              if (index_path(&ce->oid, path, st, HASH_WRITE_OBJECT)) {
                        free(ce);
                        return error("unable to index file %s", path);
                }
@@@ -733,7 -732,7 +733,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;
        }
@@@ -797,7 -796,7 +797,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;
                }
@@@ -1164,7 -1188,7 +1189,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 &&
@@@ -1500,7 -1524,6 +1525,7 @@@ struct ondisk_cache_entry_extended 
  };
  
  /* These are only used for v3 or lower */
 +#define align_padding_size(size, len) ((size + (len) + 8) & ~7) - (size + len)
  #define align_flex_name(STRUCT,len) ((offsetof(struct STRUCT,name) + (len) + 8) & ~7)
  #define ondisk_cache_entry_size(len) align_flex_name(ondisk_cache_entry,len)
  #define ondisk_cache_entry_extended_size(len) align_flex_name(ondisk_cache_entry_extended,len)
@@@ -2034,7 -2057,7 +2059,7 @@@ static void ce_smudge_racily_clean_entr
  }
  
  /* Copy miscellaneous fields but not the name */
 -static char *copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
 +static void copy_cache_entry_to_ondisk(struct ondisk_cache_entry *ondisk,
                                       struct cache_entry *ce)
  {
        short flags;
                struct ondisk_cache_entry_extended *ondisk2;
                ondisk2 = (struct ondisk_cache_entry_extended *)ondisk;
                ondisk2->flags2 = htons((ce->ce_flags & CE_EXTENDED_FLAGS) >> 16);
 -              return ondisk2->name;
 -      }
 -      else {
 -              return ondisk->name;
        }
  }
  
  static int ce_write_entry(git_SHA_CTX *c, int fd, struct cache_entry *ce,
 -                        struct strbuf *previous_name)
 +                        struct strbuf *previous_name, struct ondisk_cache_entry *ondisk)
  {
        int size;
 -      struct ondisk_cache_entry *ondisk;
        int saved_namelen = saved_namelen; /* compiler workaround */
 -      char *name;
        int result;
 +      static unsigned char padding[8] = { 0x00 };
  
        if (ce->ce_flags & CE_STRIP_NAME) {
                saved_namelen = ce_namelen(ce);
                ce->ce_namelen = 0;
        }
  
 +      if (ce->ce_flags & CE_EXTENDED)
 +              size = offsetof(struct ondisk_cache_entry_extended, name);
 +      else
 +              size = offsetof(struct ondisk_cache_entry, name);
 +
        if (!previous_name) {
 -              size = ondisk_ce_size(ce);
 -              ondisk = xcalloc(1, size);
 -              name = copy_cache_entry_to_ondisk(ondisk, ce);
 -              memcpy(name, ce->name, ce_namelen(ce));
 +              int len = ce_namelen(ce);
 +              copy_cache_entry_to_ondisk(ondisk, ce);
 +              result = ce_write(c, fd, ondisk, size);
 +              if (!result)
 +                      result = ce_write(c, fd, ce->name, len);
 +              if (!result)
 +                      result = ce_write(c, fd, padding, align_padding_size(size, len));
        } else {
                int common, to_remove, prefix_size;
                unsigned char to_remove_vi[16];
                to_remove = previous_name->len - common;
                prefix_size = encode_varint(to_remove, to_remove_vi);
  
 -              if (ce->ce_flags & CE_EXTENDED)
 -                      size = offsetof(struct ondisk_cache_entry_extended, name);
 -              else
 -                      size = offsetof(struct ondisk_cache_entry, name);
 -              size += prefix_size + (ce_namelen(ce) - common + 1);
 -
 -              ondisk = xcalloc(1, size);
 -              name = copy_cache_entry_to_ondisk(ondisk, ce);
 -              memcpy(name, to_remove_vi, prefix_size);
 -              memcpy(name + prefix_size, ce->name + common, ce_namelen(ce) - common);
 +              copy_cache_entry_to_ondisk(ondisk, ce);
 +              result = ce_write(c, fd, ondisk, size);
 +              if (!result)
 +                      result = ce_write(c, fd, to_remove_vi, prefix_size);
 +              if (!result)
 +                      result = ce_write(c, fd, ce->name + common, ce_namelen(ce) - common);
 +              if (!result)
 +                      result = ce_write(c, fd, padding, 1);
  
                strbuf_splice(previous_name, common, to_remove,
                              ce->name + common, ce_namelen(ce) - common);
                ce->ce_flags &= ~CE_STRIP_NAME;
        }
  
 -      result = ce_write(c, fd, ondisk, size);
 -      free(ondisk);
        return result;
  }
  
@@@ -2193,11 -2217,10 +2218,11 @@@ static int do_write_index(struct index_
        int newfd = tempfile->fd;
        git_SHA_CTX c;
        struct cache_header hdr;
 -      int i, err, removed, extended, hdr_version;
 +      int i, err = 0, removed, extended, hdr_version;
        struct cache_entry **cache = istate->cache;
        int entries = istate->cache_nr;
        struct stat st;
 +      struct ondisk_cache_entry_extended ondisk;
        struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
        int drop_cache_tree = 0;
  
                return -1;
  
        previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
 +
        for (i = 0; i < entries; i++) {
                struct cache_entry *ce = cache[i];
                if (ce->ce_flags & CE_REMOVE)
                        if (allow)
                                warning(msg, ce->name);
                        else
 -                              return error(msg, ce->name);
 +                              err = error(msg, ce->name);
  
                        drop_cache_tree = 1;
                }
 -              if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
 -                      return -1;
 +              if (ce_write_entry(&c, newfd, ce, previous_name, (struct ondisk_cache_entry *)&ondisk) < 0)
 +                      err = -1;
 +
 +              if (err)
 +                      break;
        }
        strbuf_release(&previous_name_buf);
  
 +      if (err)
 +              return err;
 +
        /* Write extension data here */
        if (!strip_extensions && istate->split_index) {
                struct strbuf sb = STRBUF_INIT;
  
        if (ce_flush(&c, newfd, istate->sha1))
                return -1;
 -      if (close_tempfile(tempfile))
 -              return error(_("could not close '%s'"), tempfile->filename.buf);
 +      if (close_tempfile_gently(tempfile)) {
 +              error(_("could not close '%s'"), tempfile->filename.buf);
 +              delete_tempfile(&tempfile);
 +              return -1;
 +      }
        if (stat(tempfile->filename.buf, &st))
                return -1;
        istate->timestamp.sec = (unsigned int)st.st_mtime;
@@@ -2340,7 -2353,7 +2365,7 @@@ static int commit_locked_index(struct l
  static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
                                 unsigned flags)
  {
 -      int ret = do_write_index(istate, &lock->tempfile, 0);
 +      int ret = do_write_index(istate, lock->tempfile, 0);
        if (ret)
                return ret;
        assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
        if (flags & COMMIT_LOCK)
                return commit_locked_index(lock);
        else if (flags & CLOSE_LOCK)
 -              return close_lock_file(lock);
 +              return close_lock_file_gently(lock);
        else
                return ret;
  }
@@@ -2423,33 -2436,34 +2448,33 @@@ static int clean_shared_index_files(con
        return 0;
  }
  
 -static struct tempfile temporary_sharedindex;
 -
  static int write_shared_index(struct index_state *istate,
                              struct lock_file *lock, unsigned flags)
  {
 +      struct tempfile *temp;
        struct split_index *si = istate->split_index;
 -      int fd, ret;
 +      int ret;
  
 -      fd = mks_tempfile(&temporary_sharedindex, git_path("sharedindex_XXXXXX"));
 -      if (fd < 0) {
 +      temp = mks_tempfile(git_path("sharedindex_XXXXXX"));
 +      if (!temp) {
                hashclr(si->base_sha1);
                return do_write_locked_index(istate, lock, flags);
        }
        move_cache_to_base_index(istate);
 -      ret = do_write_index(si->base, &temporary_sharedindex, 1);
 +      ret = do_write_index(si->base, temp, 1);
        if (ret) {
 -              delete_tempfile(&temporary_sharedindex);
 +              delete_tempfile(&temp);
                return ret;
        }
 -      ret = adjust_shared_perm(get_tempfile_path(&temporary_sharedindex));
 +      ret = adjust_shared_perm(get_tempfile_path(temp));
        if (ret) {
                int save_errno = errno;
 -              error("cannot fix permission bits on %s", get_tempfile_path(&temporary_sharedindex));
 -              delete_tempfile(&temporary_sharedindex);
 +              error("cannot fix permission bits on %s", get_tempfile_path(temp));
 +              delete_tempfile(&temp);
                errno = save_errno;
                return ret;
        }
 -      ret = rename_tempfile(&temporary_sharedindex,
 +      ret = rename_tempfile(&temp,
                              git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
        if (!ret) {
                hashcpy(si->base_sha1, si->base->sha1);
diff --combined submodule-config.c
index 2aa8a1747f8586839aa3036fbbc59f6c716c6128,acb7767d3704bafd5ec555daf17f2cb37d33e2a7..aa026417678ff51117fcd1151aeb16d6719a1bb5
@@@ -18,7 -18,6 +18,7 @@@ struct submodule_cache 
        struct hashmap for_path;
        struct hashmap for_name;
        unsigned initialized:1;
 +      unsigned gitmodules_read:1;
  };
  
  /*
@@@ -36,25 -35,19 +36,25 @@@ enum lookup_type 
  };
  
  static int config_path_cmp(const void *unused_cmp_data,
 -                         const struct submodule_entry *a,
 -                         const struct submodule_entry *b,
 +                         const void *entry,
 +                         const void *entry_or_key,
                           const void *unused_keydata)
  {
 +      const struct submodule_entry *a = entry;
 +      const struct submodule_entry *b = entry_or_key;
 +
        return strcmp(a->config->path, b->config->path) ||
               hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
  }
  
  static int config_name_cmp(const void *unused_cmp_data,
 -                         const struct submodule_entry *a,
 -                         const struct submodule_entry *b,
 +                         const void *entry,
 +                         const void *entry_or_key,
                           const void *unused_keydata)
  {
 +      const struct submodule_entry *a = entry;
 +      const struct submodule_entry *b = entry_or_key;
 +
        return strcmp(a->config->name, b->config->name) ||
               hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
  }
@@@ -66,8 -59,8 +66,8 @@@ static struct submodule_cache *submodul
  
  static void submodule_cache_init(struct submodule_cache *cache)
  {
 -      hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, NULL, 0);
 -      hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, NULL, 0);
 +      hashmap_init(&cache->for_path, config_path_cmp, NULL, 0);
 +      hashmap_init(&cache->for_name, config_name_cmp, NULL, 0);
        cache->initialized = 1;
  }
  
@@@ -100,7 -93,6 +100,7 @@@ static void submodule_cache_clear(struc
        hashmap_free(&cache->for_path, 1);
        hashmap_free(&cache->for_name, 1);
        cache->initialized = 0;
 +      cache->gitmodules_read = 0;
  }
  
  void submodule_cache_free(struct submodule_cache *cache)
@@@ -190,6 -182,31 +190,31 @@@ static struct submodule *cache_lookup_n
        return NULL;
  }
  
+ int check_submodule_name(const char *name)
+ {
+       /* Disallow empty names */
+       if (!*name)
+               return -1;
+       /*
+        * Look for '..' as a path component. Check both '/' and '\\' as
+        * separators rather than is_dir_sep(), because we want the name rules
+        * to be consistent across platforms.
+        */
+       goto in_component; /* always start inside component */
+       while (*name) {
+               char c = *name++;
+               if (c == '/' || c == '\\') {
+ in_component:
+                       if (name[0] == '.' && name[1] == '.' &&
+                           (!name[2] || name[2] == '/' || name[2] == '\\'))
+                               return -1;
+               }
+       }
+       return 0;
+ }
  static int name_and_item_from_var(const char *var, struct strbuf *name,
                                  struct strbuf *item)
  {
                return 0;
  
        strbuf_add(name, subsection, subsection_len);
+       if (check_submodule_name(name->buf) < 0) {
+               warning(_("ignoring suspicious submodule name: %s"), name->buf);
+               strbuf_release(name);
+               return 0;
+       }
        strbuf_addstr(item, key);
  
        return 1;
@@@ -240,7 -263,7 +271,7 @@@ static struct submodule *lookup_or_crea
  static int parse_fetch_recurse(const char *opt, const char *arg,
                               int die_on_error)
  {
 -      switch (git_config_maybe_bool(opt, arg)) {
 +      switch (git_parse_maybe_bool(arg)) {
        case 1:
                return RECURSE_SUBMODULES_ON;
        case 0:
        }
  }
  
 +int parse_submodule_fetchjobs(const char *var, const char *value)
 +{
 +      int fetchjobs = git_config_int(var, value);
 +      if (fetchjobs < 0)
 +              die(_("negative values not allowed for submodule.fetchjobs"));
 +      return fetchjobs;
 +}
 +
  int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
  {
        return parse_fetch_recurse(opt, arg, 1);
@@@ -293,7 -308,7 +324,7 @@@ int option_fetch_parse_recurse_submodul
  static int parse_update_recurse(const char *opt, const char *arg,
                                int die_on_error)
  {
 -      switch (git_config_maybe_bool(opt, arg)) {
 +      switch (git_parse_maybe_bool(arg)) {
        case 1:
                return RECURSE_SUBMODULES_ON;
        case 0:
@@@ -313,7 -328,7 +344,7 @@@ int parse_update_recurse_submodules_arg
  static int parse_push_recurse(const char *opt, const char *arg,
                               int die_on_error)
  {
 -      switch (git_config_maybe_bool(opt, arg)) {
 +      switch (git_parse_maybe_bool(arg)) {
        case 1:
                /* There's no simple "on" value when pushing */
                if (die_on_error)
@@@ -457,19 -472,19 +488,19 @@@ static int parse_config(const char *var
        return ret;
  }
  
 -int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
 -                                    unsigned char *gitmodules_sha1,
 -                                    struct strbuf *rev)
 +static int gitmodule_oid_from_commit(const struct object_id *treeish_name,
 +                                   struct object_id *gitmodules_oid,
 +                                   struct strbuf *rev)
  {
        int ret = 0;
  
 -      if (is_null_sha1(treeish_name)) {
 -              hashclr(gitmodules_sha1);
 +      if (is_null_oid(treeish_name)) {
 +              oidclr(gitmodules_oid);
                return 1;
        }
  
 -      strbuf_addf(rev, "%s:.gitmodules", sha1_to_hex(treeish_name));
 -      if (get_sha1(rev->buf, gitmodules_sha1) >= 0)
 +      strbuf_addf(rev, "%s:.gitmodules", oid_to_hex(treeish_name));
 +      if (get_oid(rev->buf, gitmodules_oid) >= 0)
                ret = 1;
  
        return ret;
   * revisions.
   */
  static const struct submodule *config_from(struct submodule_cache *cache,
 -              const unsigned char *treeish_name, const char *key,
 +              const struct object_id *treeish_name, const char *key,
                enum lookup_type lookup_type)
  {
        struct strbuf rev = STRBUF_INIT;
        unsigned long config_size;
        char *config = NULL;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        enum object_type type;
        const struct submodule *submodule = NULL;
        struct parse_config_parameter parameter;
                return entry->config;
        }
  
 -      if (!gitmodule_sha1_from_commit(treeish_name, sha1, &rev))
 +      if (!gitmodule_oid_from_commit(treeish_name, &oid, &rev))
                goto out;
  
        switch (lookup_type) {
        case lookup_name:
 -              submodule = cache_lookup_name(cache, sha1, key);
 +              submodule = cache_lookup_name(cache, oid.hash, key);
                break;
        case lookup_path:
 -              submodule = cache_lookup_path(cache, sha1, key);
 +              submodule = cache_lookup_path(cache, oid.hash, key);
                break;
        }
        if (submodule)
                goto out;
  
 -      config = read_sha1_file(sha1, &type, &config_size);
 +      config = read_sha1_file(oid.hash, &type, &config_size);
        if (!config || type != OBJ_BLOB)
                goto out;
  
        /* fill the submodule config into the cache */
        parameter.cache = cache;
 -      parameter.treeish_name = treeish_name;
 -      parameter.gitmodules_sha1 = sha1;
 +      parameter.treeish_name = treeish_name->hash;
 +      parameter.gitmodules_sha1 = oid.hash;
        parameter.overwrite = 0;
        git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
                        config, config_size, &parameter);
  
        switch (lookup_type) {
        case lookup_name:
 -              return cache_lookup_name(cache, sha1, key);
 +              return cache_lookup_name(cache, oid.hash, key);
        case lookup_path:
 -              return cache_lookup_path(cache, sha1, key);
 +              return cache_lookup_path(cache, oid.hash, key);
        default:
                return NULL;
        }
@@@ -560,11 -575,13 +591,11 @@@ static void submodule_cache_check_init(
        submodule_cache_init(repo->submodule_cache);
  }
  
 -int submodule_config_option(struct repository *repo,
 -                          const char *var, const char *value)
 +static int gitmodules_cb(const char *var, const char *value, void *data)
  {
 +      struct repository *repo = data;
        struct parse_config_parameter parameter;
  
 -      submodule_cache_check_init(repo);
 -
        parameter.cache = repo->submodule_cache;
        parameter.treeish_name = NULL;
        parameter.gitmodules_sha1 = null_sha1;
        return parse_config(var, value, &parameter);
  }
  
 -int parse_submodule_config_option(const char *var, const char *value)
 +void repo_read_gitmodules(struct repository *repo)
  {
 -      return submodule_config_option(the_repository, var, value);
 +      submodule_cache_check_init(repo);
 +
 +      if (repo->worktree) {
 +              char *gitmodules;
 +
 +              if (repo_read_index(repo) < 0)
 +                      return;
 +
 +              gitmodules = repo_worktree_path(repo, GITMODULES_FILE);
 +
 +              if (!is_gitmodules_unmerged(repo->index))
 +                      git_config_from_file(gitmodules_cb, gitmodules, repo);
 +
 +              free(gitmodules);
 +      }
 +
 +      repo->submodule_cache->gitmodules_read = 1;
  }
  
 -const struct submodule *submodule_from_name(const unsigned char *treeish_name,
 -              const char *name)
 +void gitmodules_config_oid(const struct object_id *commit_oid)
  {
 +      struct strbuf rev = STRBUF_INIT;
 +      struct object_id oid;
 +
        submodule_cache_check_init(the_repository);
 +
 +      if (gitmodule_oid_from_commit(commit_oid, &oid, &rev)) {
 +              git_config_from_blob_oid(gitmodules_cb, rev.buf,
 +                                       &oid, the_repository);
 +      }
 +      strbuf_release(&rev);
 +
 +      the_repository->submodule_cache->gitmodules_read = 1;
 +}
 +
 +static void gitmodules_read_check(struct repository *repo)
 +{
 +      submodule_cache_check_init(repo);
 +
 +      /* read the repo's .gitmodules file if it hasn't been already */
 +      if (!repo->submodule_cache->gitmodules_read)
 +              repo_read_gitmodules(repo);
 +}
 +
 +const struct submodule *submodule_from_name(const struct object_id *treeish_name,
 +              const char *name)
 +{
 +      gitmodules_read_check(the_repository);
        return config_from(the_repository->submodule_cache, treeish_name, name, lookup_name);
  }
  
 -const struct submodule *submodule_from_path(const unsigned char *treeish_name,
 +const struct submodule *submodule_from_path(const struct object_id *treeish_name,
                const char *path)
  {
 -      submodule_cache_check_init(the_repository);
 +      gitmodules_read_check(the_repository);
        return config_from(the_repository->submodule_cache, treeish_name, path, lookup_path);
  }
  
  const struct submodule *submodule_from_cache(struct repository *repo,
 -                                           const unsigned char *treeish_name,
 +                                           const struct object_id *treeish_name,
                                             const char *key)
  {
 -      submodule_cache_check_init(repo);
 +      gitmodules_read_check(repo);
        return config_from(repo->submodule_cache, treeish_name,
                           key, lookup_path);
  }
diff --combined submodule-config.h
index e3845831f6cbd25097ea54b3e2246faa8a32e192,634fe39565cedfb4f242322d74a7096ead339556..93880cf0217aa3516c1f435934b8adad25ae2867
@@@ -27,22 -27,32 +27,29 @@@ struct repository
  
  extern void submodule_cache_free(struct submodule_cache *cache);
  
 +extern int parse_submodule_fetchjobs(const char *var, const char *value);
  extern int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
  struct option;
  extern int option_fetch_parse_recurse_submodules(const struct option *opt,
                                                 const char *arg, int unset);
  extern int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
  extern int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
 -extern int parse_submodule_config_option(const char *var, const char *value);
 -extern int submodule_config_option(struct repository *repo,
 -                                 const char *var, const char *value);
 +extern void repo_read_gitmodules(struct repository *repo);
 +extern void gitmodules_config_oid(const struct object_id *commit_oid);
  extern const struct submodule *submodule_from_name(
 -              const unsigned char *commit_or_tree, const char *name);
 +              const struct object_id *commit_or_tree, const char *name);
  extern const struct submodule *submodule_from_path(
 -              const unsigned char *commit_or_tree, const char *path);
 +              const struct object_id *commit_or_tree, const char *path);
  extern const struct submodule *submodule_from_cache(struct repository *repo,
 -                                                  const unsigned char *treeish_name,
 +                                                  const struct object_id *treeish_name,
                                                    const char *key);
 -extern int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
 -                                    unsigned char *gitmodules_sha1,
 -                                    struct strbuf *rev);
  extern void submodule_free(void);
  
+ /*
+  * Returns 0 if the name is syntactically acceptable as a submodule "name"
+  * (e.g., that may be found in the subsection of a .gitmodules file) and -1
+  * otherwise.
+  */
+ int check_submodule_name(const char *name);
  #endif /* SUBMODULE_CONFIG_H */
diff --combined utf8.c
index 2c27ce0137f8a60ca2fadf855f2c67738931e2f8,cbf22a71d74beccc3912526e9bb61ca296e07759..f04c24409b4e95d69ccd08473ef4c43d408d6d86
--- 1/utf8.c
--- 2/utf8.c
+++ b/utf8.c
@@@ -32,7 -32,7 +32,7 @@@ static int bisearch(ucs_char_t ucs, con
        if (ucs < table[0].first || ucs > table[max].last)
                return 0;
        while (max >= min) {
 -              mid = (min + max) / 2;
 +              mid = min + (max - min) / 2;
                if (ucs > table[mid].last)
                        min = mid + 1;
                else if (ucs < table[mid].first)
@@@ -381,7 -381,7 +381,7 @@@ void strbuf_utf8_replace(struct strbuf 
                old = src;
                n = utf8_width((const char**)&src, NULL);
                if (!src)       /* broken utf-8, do nothing */
 -                      return;
 +                      goto out;
                if (n && w >= pos && w < pos + width) {
                        if (subst) {
                                memcpy(dst, subst, subst_len);
        }
        strbuf_setlen(&sb_dst, dst - sb_dst.buf);
        strbuf_swap(sb_src, &sb_dst);
 +out:
        strbuf_release(&sb_dst);
  }
  
@@@ -620,28 -619,33 +620,33 @@@ static ucs_char_t next_hfs_char(const c
        }
  }
  
- int is_hfs_dotgit(const char *path)
+ static int is_hfs_dot_generic(const char *path,
+                             const char *needle, size_t needle_len)
  {
        ucs_char_t c;
  
        c = next_hfs_char(&path);
        if (c != '.')
                return 0;
-       c = next_hfs_char(&path);
  
        /*
         * there's a great deal of other case-folding that occurs
-        * in HFS+, but this is enough to catch anything that will
-        * convert to ".git"
+        * in HFS+, but this is enough to catch our fairly vanilla
+        * hard-coded needles.
         */
-       if (c != 'g' && c != 'G')
-               return 0;
-       c = next_hfs_char(&path);
-       if (c != 'i' && c != 'I')
-               return 0;
-       c = next_hfs_char(&path);
-       if (c != 't' && c != 'T')
-               return 0;
+       for (; needle_len > 0; needle++, needle_len--) {
+               c = next_hfs_char(&path);
+               /*
+                * We know our needles contain only ASCII, so we clamp here to
+                * make the results of tolower() sane.
+                */
+               if (c > 127)
+                       return 0;
+               if (tolower(c) != *needle)
+                       return 0;
+       }
        c = next_hfs_char(&path);
        if (c && !is_dir_sep(c))
                return 0;
        return 1;
  }
  
+ /*
+  * Inline wrapper to make sure the compiler resolves strlen() on literals at
+  * compile time.
+  */
+ static inline int is_hfs_dot_str(const char *path, const char *needle)
+ {
+       return is_hfs_dot_generic(path, needle, strlen(needle));
+ }
+ int is_hfs_dotgit(const char *path)
+ {
+       return is_hfs_dot_str(path, "git");
+ }
+ int is_hfs_dotgitmodules(const char *path)
+ {
+       return is_hfs_dot_str(path, "gitmodules");
+ }
+ int is_hfs_dotgitignore(const char *path)
+ {
+       return is_hfs_dot_str(path, "gitignore");
+ }
+ int is_hfs_dotgitattributes(const char *path)
+ {
+       return is_hfs_dot_str(path, "gitattributes");
+ }
  const char utf8_bom[] = "\357\273\277";
  
  int skip_utf8_bom(char **text, size_t len)