Merge 'hv/submodule-config' to 'sb/submodule-helper'
authorJunio C Hamano <gitster@pobox.com>
Wed, 19 Aug 2015 18:44:39 +0000 (11:44 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 19 Aug 2015 18:45:08 +0000 (11:45 -0700)
* hv/submodule-config:
submodule: allow erroneous values for the fetchRecurseSubmodules option
submodule: use new config API for worktree configurations
submodule: extract functions for config set and lookup
submodule: implement a config API for lookup of .gitmodules values

1  2 
.gitignore
Makefile
builtin/checkout.c
builtin/fetch.c
diff.c
submodule.c
diff --combined .gitignore
index a685ec1fb0ca49607431a65f1ccf035bb9b95a3a,337c1215c1e939d76b16abd7d30e44f7bc52765b..4fd81baf856669fb984a5a0b82a1115e840fc16d
  /git-verify-tag
  /git-web--browse
  /git-whatchanged
 +/git-worktree
  /git-write-tree
  /git-core-*/?*
  /gitweb/GITWEB-BUILD-OPTIONS
  /test-sha1-array
  /test-sigchain
  /test-string-list
+ /test-submodule-config
  /test-subprocess
  /test-svn-fe
  /test-urlmatch-normalization
diff --combined Makefile
index 7efedbe8346f198716674161248fda43396c656b,5d9a63fb3c73e55d7823b6d475bf9cdacab65234..24b636dc58588df1f5d7168b286189b1b018743e
+++ b/Makefile
@@@ -217,11 -217,10 +217,11 @@@ all:
  # as the compiler can crash (http://gcc.gnu.org/bugzilla/show_bug.cgi?id=49299)
  #
  # Define USE_NSEC below if you want git to care about sub-second file mtimes
 -# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and
 -# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely
 -# randomly break unless your underlying filesystem supports those sub-second
 -# times (my ext3 doesn't).
 +# and ctimes. Note that you need recent glibc (at least 2.2.4) for this. On
 +# Linux, kernel 2.6.11 or newer is required for reliable sub-second file times
 +# on file systems with exactly 1 ns or 1 s resolution. If you intend to use Git
 +# on other file systems (e.g. CEPH, CIFS, NTFS, UDF), don't enable USE_NSEC. See
 +# Documentation/technical/racy-git.txt for details.
  #
  # Define USE_ST_TIMESPEC if your "struct stat" uses "st_ctimespec" instead of
  # "st_ctim"
@@@ -475,6 -474,7 +475,6 @@@ SCRIPT_SH += git-merge-octopus.s
  SCRIPT_SH += git-merge-one-file.sh
  SCRIPT_SH += git-merge-resolve.sh
  SCRIPT_SH += git-mergetool.sh
 -SCRIPT_SH += git-pull.sh
  SCRIPT_SH += git-quiltimport.sh
  SCRIPT_SH += git-rebase.sh
  SCRIPT_SH += git-remote-testgit.sh
@@@ -594,6 -594,7 +594,7 @@@ TEST_PROGRAMS_NEED_X += test-sha
  TEST_PROGRAMS_NEED_X += test-sha1-array
  TEST_PROGRAMS_NEED_X += test-sigchain
  TEST_PROGRAMS_NEED_X += test-string-list
+ TEST_PROGRAMS_NEED_X += test-submodule-config
  TEST_PROGRAMS_NEED_X += test-subprocess
  TEST_PROGRAMS_NEED_X += test-svn-fe
  TEST_PROGRAMS_NEED_X += test-urlmatch-normalization
@@@ -762,7 -763,6 +763,7 @@@ LIB_OBJS += reachable.
  LIB_OBJS += read-cache.o
  LIB_OBJS += reflog-walk.o
  LIB_OBJS += refs.o
 +LIB_OBJS += ref-filter.o
  LIB_OBJS += remote.o
  LIB_OBJS += replace_object.o
  LIB_OBJS += rerere.o
@@@ -785,6 -785,7 +786,7 @@@ LIB_OBJS += strbuf.
  LIB_OBJS += streaming.o
  LIB_OBJS += string-list.o
  LIB_OBJS += submodule.o
+ LIB_OBJS += submodule-config.o
  LIB_OBJS += symlinks.o
  LIB_OBJS += tag.o
  LIB_OBJS += trace.o
@@@ -878,7 -879,6 +880,7 @@@ BUILTIN_OBJS += builtin/pack-refs.
  BUILTIN_OBJS += builtin/patch-id.o
  BUILTIN_OBJS += builtin/prune-packed.o
  BUILTIN_OBJS += builtin/prune.o
 +BUILTIN_OBJS += builtin/pull.o
  BUILTIN_OBJS += builtin/push.o
  BUILTIN_OBJS += builtin/read-tree.o
  BUILTIN_OBJS += builtin/receive-pack.o
@@@ -911,7 -911,6 +913,7 @@@ BUILTIN_OBJS += builtin/var.
  BUILTIN_OBJS += builtin/verify-commit.o
  BUILTIN_OBJS += builtin/verify-pack.o
  BUILTIN_OBJS += builtin/verify-tag.o
 +BUILTIN_OBJS += builtin/worktree.o
  BUILTIN_OBJS += builtin/write-tree.o
  
  GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
@@@ -1750,7 -1749,7 +1752,7 @@@ $(SCRIPT_PERL_GEN): perl/perl.ma
  perl/perl.mak: perl/PM.stamp
  
  perl/PM.stamp: FORCE
 -      $(QUIET_GEN)$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
 +      @$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
        { cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@; } && \
        $(RM) $@+
  
@@@ -1787,7 -1786,7 +1789,7 @@@ GIT-PERL-DEFINES: FORC
  gitweb:
        $(QUIET_SUBDIR0)gitweb $(QUIET_SUBDIR1) all
  
 -git-instaweb: git-instaweb.sh gitweb GIT-SCRIPT-DEFINES
 +git-instaweb: git-instaweb.sh GIT-SCRIPT-DEFINES
        $(QUIET_GEN)$(cmd_munge_script) && \
        chmod +x $@+ && \
        mv $@+ $@
@@@ -2106,47 -2105,46 +2108,47 @@@ GIT-LDFLAGS: FORC
  # that runs GIT-BUILD-OPTIONS, and then again to protect it
  # and the first level quoting from the shell that runs "echo".
  GIT-BUILD-OPTIONS: FORCE
 -      @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
 -      @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
 -      @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@
 -      @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@
 -      @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
 -      @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
 -      @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@
 -      @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@
 -      @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
 -      @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
 -      @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
 +      @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@+
 +      @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@+
 +      @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@+
 +      @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@+
 +      @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@+
 +      @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@+
 +      @echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+
 +      @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+
 +      @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
 +      @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
 +      @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
  ifdef TEST_OUTPUT_DIRECTORY
 -      @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@
 +      @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
  endif
  ifdef GIT_TEST_OPTS
 -      @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@
 +      @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@+
  endif
  ifdef GIT_TEST_CMP
 -      @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@
 +      @echo GIT_TEST_CMP=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_CMP)))'\' >>$@+
  endif
  ifdef GIT_TEST_CMP_USE_COPIED_CONTEXT
 -      @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@
 +      @echo GIT_TEST_CMP_USE_COPIED_CONTEXT=YesPlease >>$@+
  endif
 -      @echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@
 -      @echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@
 +      @echo NO_GETTEXT=\''$(subst ','\'',$(subst ','\'',$(NO_GETTEXT)))'\' >>$@+
 +      @echo GETTEXT_POISON=\''$(subst ','\'',$(subst ','\'',$(GETTEXT_POISON)))'\' >>$@+
  ifdef GIT_PERF_REPEAT_COUNT
 -      @echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@
 +      @echo GIT_PERF_REPEAT_COUNT=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPEAT_COUNT)))'\' >>$@+
  endif
  ifdef GIT_PERF_REPO
 -      @echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@
 +      @echo GIT_PERF_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_REPO)))'\' >>$@+
  endif
  ifdef GIT_PERF_LARGE_REPO
 -      @echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@
 +      @echo GIT_PERF_LARGE_REPO=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_LARGE_REPO)))'\' >>$@+
  endif
  ifdef GIT_PERF_MAKE_OPTS
 -      @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@
 +      @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+
  endif
  ifdef TEST_GIT_INDEX_VERSION
 -      @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@
 +      @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+
  endif
 +      @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
  
  ### Detect Python interpreter path changes
  ifndef NO_PYTHON
diff --combined builtin/checkout.c
index 9ff5ca4cbe97dd2dcd2cfa261d7f778809a88133,f1f168d2f60ab76a10641b57d9c0834f03a228a0..7ea533e4ab6a244297068fe8a376c094e7d42291
  #include "xdiff-interface.h"
  #include "ll-merge.h"
  #include "resolve-undo.h"
+ #include "submodule-config.h"
  #include "submodule.h"
 -#include "argv-array.h"
 -#include "sigchain.h"
  
  static const char * const checkout_usage[] = {
        N_("git checkout [<options>] <branch>"),
@@@ -49,6 -52,8 +50,6 @@@ struct checkout_opts 
        struct pathspec pathspec;
        struct tree *source_tree;
  
 -      const char *new_worktree;
 -      const char **saved_argv;
        int new_worktree_mode;
  };
  
@@@ -269,6 -274,9 +270,6 @@@ static int checkout_paths(const struct 
                die(_("Cannot update paths and switch to branch '%s' at the same time."),
                    opts->new_branch);
  
 -      if (opts->new_worktree)
 -              die(_("'%s' cannot be used with updating paths"), "--to");
 -
        if (opts->patch_mode)
                return run_add_interactive(revision, "--patch=checkout",
                                           &opts->pathspec);
@@@ -612,20 -620,22 +613,20 @@@ static void update_refs_for_switch(cons
        if (opts->new_branch) {
                if (opts->new_orphan_branch) {
                        if (opts->new_branch_log && !log_all_ref_updates) {
 -                              int temp;
 -                              struct strbuf log_file = STRBUF_INIT;
                                int ret;
 -                              const char *ref_name;
 -
 -                              ref_name = mkpath("refs/heads/%s", opts->new_orphan_branch);
 -                              temp = log_all_ref_updates;
 -                              log_all_ref_updates = 1;
 -                              ret = log_ref_setup(ref_name, &log_file);
 -                              log_all_ref_updates = temp;
 -                              strbuf_release(&log_file);
 +                              char *refname;
 +                              struct strbuf err = STRBUF_INIT;
 +
 +                              refname = mkpathdup("refs/heads/%s", opts->new_orphan_branch);
 +                              ret = safe_create_reflog(refname, 1, &err);
 +                              free(refname);
                                if (ret) {
 -                                      fprintf(stderr, _("Can not do reflog for '%s'\n"),
 -                                          opts->new_orphan_branch);
 +                                      fprintf(stderr, _("Can not do reflog for '%s': %s\n"),
 +                                              opts->new_orphan_branch, err.buf);
 +                                      strbuf_release(&err);
                                        return;
                                }
 +                              strbuf_release(&err);
                        }
                }
                else
  }
  
  static int add_pending_uninteresting_ref(const char *refname,
 -                                       const unsigned char *sha1,
 +                                       const struct object_id *oid,
                                         int flags, void *cb_data)
  {
 -      add_pending_sha1(cb_data, refname, sha1, UNINTERESTING);
 +      add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING);
        return 0;
  }
  
@@@ -841,6 -851,138 +842,6 @@@ static int switch_branches(const struc
        return ret || writeout_error;
  }
  
 -static char *junk_work_tree;
 -static char *junk_git_dir;
 -static int is_junk;
 -static pid_t junk_pid;
 -
 -static void remove_junk(void)
 -{
 -      struct strbuf sb = STRBUF_INIT;
 -      if (!is_junk || getpid() != junk_pid)
 -              return;
 -      if (junk_git_dir) {
 -              strbuf_addstr(&sb, junk_git_dir);
 -              remove_dir_recursively(&sb, 0);
 -              strbuf_reset(&sb);
 -      }
 -      if (junk_work_tree) {
 -              strbuf_addstr(&sb, junk_work_tree);
 -              remove_dir_recursively(&sb, 0);
 -      }
 -      strbuf_release(&sb);
 -}
 -
 -static void remove_junk_on_signal(int signo)
 -{
 -      remove_junk();
 -      sigchain_pop(signo);
 -      raise(signo);
 -}
 -
 -static int prepare_linked_checkout(const struct checkout_opts *opts,
 -                                 struct branch_info *new)
 -{
 -      struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
 -      struct strbuf sb = STRBUF_INIT;
 -      const char *path = opts->new_worktree, *name;
 -      struct stat st;
 -      struct child_process cp;
 -      int counter = 0, len, ret;
 -
 -      if (!new->commit)
 -              die(_("no branch specified"));
 -      if (file_exists(path) && !is_empty_dir(path))
 -              die(_("'%s' already exists"), path);
 -
 -      len = strlen(path);
 -      while (len && is_dir_sep(path[len - 1]))
 -              len--;
 -
 -      for (name = path + len - 1; name > path; name--)
 -              if (is_dir_sep(*name)) {
 -                      name++;
 -                      break;
 -              }
 -      strbuf_addstr(&sb_repo,
 -                    git_path("worktrees/%.*s", (int)(path + len - name), name));
 -      len = sb_repo.len;
 -      if (safe_create_leading_directories_const(sb_repo.buf))
 -              die_errno(_("could not create leading directories of '%s'"),
 -                        sb_repo.buf);
 -      while (!stat(sb_repo.buf, &st)) {
 -              counter++;
 -              strbuf_setlen(&sb_repo, len);
 -              strbuf_addf(&sb_repo, "%d", counter);
 -      }
 -      name = strrchr(sb_repo.buf, '/') + 1;
 -
 -      junk_pid = getpid();
 -      atexit(remove_junk);
 -      sigchain_push_common(remove_junk_on_signal);
 -
 -      if (mkdir(sb_repo.buf, 0777))
 -              die_errno(_("could not create directory of '%s'"), sb_repo.buf);
 -      junk_git_dir = xstrdup(sb_repo.buf);
 -      is_junk = 1;
 -
 -      /*
 -       * lock the incomplete repo so prune won't delete it, unlock
 -       * after the preparation is over.
 -       */
 -      strbuf_addf(&sb, "%s/locked", sb_repo.buf);
 -      write_file(sb.buf, 1, "initializing\n");
 -
 -      strbuf_addf(&sb_git, "%s/.git", path);
 -      if (safe_create_leading_directories_const(sb_git.buf))
 -              die_errno(_("could not create leading directories of '%s'"),
 -                        sb_git.buf);
 -      junk_work_tree = xstrdup(path);
 -
 -      strbuf_reset(&sb);
 -      strbuf_addf(&sb, "%s/gitdir", sb_repo.buf);
 -      write_file(sb.buf, 1, "%s\n", real_path(sb_git.buf));
 -      write_file(sb_git.buf, 1, "gitdir: %s/worktrees/%s\n",
 -                 real_path(get_git_common_dir()), name);
 -      /*
 -       * This is to keep resolve_ref() happy. We need a valid HEAD
 -       * or is_git_directory() will reject the directory. Any valid
 -       * value would do because this value will be ignored and
 -       * replaced at the next (real) checkout.
 -       */
 -      strbuf_reset(&sb);
 -      strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
 -      write_file(sb.buf, 1, "%s\n", sha1_to_hex(new->commit->object.sha1));
 -      strbuf_reset(&sb);
 -      strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
 -      write_file(sb.buf, 1, "../..\n");
 -
 -      if (!opts->quiet)
 -              fprintf_ln(stderr, _("Enter %s (identifier %s)"), path, name);
 -
 -      setenv("GIT_CHECKOUT_NEW_WORKTREE", "1", 1);
 -      setenv(GIT_DIR_ENVIRONMENT, sb_git.buf, 1);
 -      setenv(GIT_WORK_TREE_ENVIRONMENT, path, 1);
 -      memset(&cp, 0, sizeof(cp));
 -      cp.git_cmd = 1;
 -      cp.argv = opts->saved_argv;
 -      ret = run_command(&cp);
 -      if (!ret) {
 -              is_junk = 0;
 -              free(junk_work_tree);
 -              free(junk_git_dir);
 -              junk_work_tree = NULL;
 -              junk_git_dir = NULL;
 -      }
 -      strbuf_reset(&sb);
 -      strbuf_addf(&sb, "%s/locked", sb_repo.buf);
 -      unlink_or_warn(sb.buf);
 -      strbuf_release(&sb);
 -      strbuf_release(&sb_repo);
 -      strbuf_release(&sb_git);
 -      return ret;
 -}
 -
  static int git_checkout_config(const char *var, const char *value, void *cb)
  {
        if (!strcmp(var, "diff.ignoresubmodules")) {
@@@ -969,6 -1111,7 +970,6 @@@ static int parse_branchname_arg(int arg
  {
        struct tree **source_tree = &opts->source_tree;
        const char **new_branch = &opts->new_branch;
 -      int force_detach = opts->force_detach;
        int argcount = 0;
        unsigned char branch_rev[20];
        const char *arg;
        else
                new->path = NULL; /* not an existing branch */
  
 -      if (new->path && !force_detach && !*new_branch) {
 -              unsigned char sha1[20];
 -              int flag;
 -              char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
 -              if (head_ref &&
 -                  (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) &&
 -                  !opts->ignore_other_worktrees)
 -                      check_linked_checkouts(new);
 -              free(head_ref);
 -      }
 -
        new->commit = lookup_commit_reference_gently(rev, 1);
        if (!new->commit) {
                /* not a commit */
@@@ -1168,16 -1322,8 +1169,16 @@@ static int checkout_branch(struct check
                die(_("Cannot switch branch to a non-commit '%s'"),
                    new->name);
  
 -      if (opts->new_worktree)
 -              return prepare_linked_checkout(opts, new);
 +      if (new->path && !opts->force_detach && !opts->new_branch) {
 +              unsigned char sha1[20];
 +              int flag;
 +              char *head_ref = resolve_refdup("HEAD", 0, sha1, &flag);
 +              if (head_ref &&
 +                  (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)) &&
 +                  !opts->ignore_other_worktrees)
 +                      check_linked_checkouts(new);
 +              free(head_ref);
 +      }
  
        if (!new->commit && opts->new_branch) {
                unsigned char rev[20];
@@@ -1221,6 -1367,8 +1222,6 @@@ int cmd_checkout(int argc, const char *
                         N_("do not limit pathspecs to sparse entries only")),
                OPT_HIDDEN_BOOL(0, "guess", &dwim_new_local_branch,
                                N_("second guess 'git checkout <no-such-branch>'")),
 -              OPT_FILENAME(0, "to", &opts.new_worktree,
 -                         N_("check a branch out in a separate working directory")),
                OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
                         N_("do not check if another worktree is holding the given ref")),
                OPT_END(),
        opts.overwrite_ignore = 1;
        opts.prefix = prefix;
  
 -      opts.saved_argv = xmalloc(sizeof(const char *) * (argc + 2));
 -      memcpy(opts.saved_argv, argv, sizeof(const char *) * (argc + 1));
 -
        gitmodules_config();
        git_config(git_checkout_config, &opts);
  
        argc = parse_options(argc, argv, prefix, options, checkout_usage,
                             PARSE_OPT_KEEP_DASHDASH);
  
 -      /* recursive execution from checkout_new_worktree() */
        opts.new_worktree_mode = getenv("GIT_CHECKOUT_NEW_WORKTREE") != NULL;
 -      if (opts.new_worktree_mode)
 -              opts.new_worktree = NULL;
 -
 -      if (!opts.new_worktree)
 -              setup_work_tree();
  
        if (conflict_style) {
                opts.merge = 1; /* implied */
diff --combined builtin/fetch.c
index df16d0a56e97c1ebbafd3498307901754b6daf64,faae548a28230ea17c4bd6188194238272138a43..ee1f1a93af7bcab37a374bf142c601ce1c9b2315
@@@ -11,6 -11,7 +11,7 @@@
  #include "run-command.h"
  #include "parse-options.h"
  #include "sigchain.h"
+ #include "submodule-config.h"
  #include "submodule.h"
  #include "connected.h"
  #include "argv-array.h"
@@@ -179,15 -180,13 +180,15 @@@ static void add_merge_config(struct re
        }
  }
  
 -static int add_existing(const char *refname, const unsigned char *sha1,
 +static int add_existing(const char *refname, const struct object_id *oid,
                        int flag, void *cbdata)
  {
        struct string_list *list = (struct string_list *)cbdata;
        struct string_list_item *item = string_list_insert(list, refname);
 -      item->util = xmalloc(20);
 -      hashcpy(item->util, sha1);
 +      struct object_id *old_oid = xmalloc(sizeof(*old_oid));
 +
 +      oidcpy(old_oid, oid);
 +      item->util = old_oid;
        return 0;
  }
  
@@@ -591,7 -590,7 +592,7 @@@ static int store_updated_refs(const cha
        const char *what, *kind;
        struct ref *rm;
        char *url;
 -      const char *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
 +      const char *filename = dry_run ? "/dev/null" : git_path_fetch_head();
        int want_status;
  
        fp = fopen(filename, "a");
@@@ -790,29 -789,20 +791,29 @@@ static int prune_refs(struct refspec *r
        if (4 < i && !strncmp(".git", url + i - 3, 4))
                url_len = i - 3;
  
 -      for (ref = stale_refs; ref; ref = ref->next) {
 -              if (!dry_run)
 -                      result |= delete_ref(ref->name, NULL, 0);
 -              if (verbosity >= 0 && !shown_url) {
 -                      fprintf(stderr, _("From %.*s\n"), url_len, url);
 -                      shown_url = 1;
 -              }
 -              if (verbosity >= 0) {
 +      if (!dry_run) {
 +              struct string_list refnames = STRING_LIST_INIT_NODUP;
 +
 +              for (ref = stale_refs; ref; ref = ref->next)
 +                      string_list_append(&refnames, ref->name);
 +
 +              result = delete_refs(&refnames);
 +              string_list_clear(&refnames, 0);
 +      }
 +
 +      if (verbosity >= 0) {
 +              for (ref = stale_refs; ref; ref = ref->next) {
 +                      if (!shown_url) {
 +                              fprintf(stderr, _("From %.*s\n"), url_len, url);
 +                              shown_url = 1;
 +                      }
                        fprintf(stderr, " x %-*s %-*s -> %s\n",
                                TRANSPORT_SUMMARY(_("[deleted]")),
                                REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name));
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
        }
 +
        free(url);
        free_refs(stale_refs);
        return result;
@@@ -834,7 -824,7 +835,7 @@@ static void check_not_current_branch(st
  
  static int truncate_fetch_head(void)
  {
 -      const char *filename = git_path("FETCH_HEAD");
 +      const char *filename = git_path_fetch_head();
        FILE *fp = fopen(filename, "w");
  
        if (!fp)
@@@ -924,10 -914,9 +925,10 @@@ static int do_fetch(struct transport *t
                        struct string_list_item *peer_item =
                                string_list_lookup(&existing_refs,
                                                   rm->peer_ref->name);
 -                      if (peer_item)
 -                              hashcpy(rm->peer_ref->old_sha1,
 -                                      peer_item->util);
 +                      if (peer_item) {
 +                              struct object_id *old_oid = peer_item->util;
 +                              hashcpy(rm->peer_ref->old_sha1, old_oid->hash);
 +                      }
                }
        }
  
diff --combined diff.c
index 7deac90532234996088ae847b32ce07f5ba36339,d0be279c693e263728c7c97958a2a7fe81267af0..8b382c2b6b3c1d869b7e3f30f247b14024bf5b80
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -13,6 -13,7 +13,7 @@@
  #include "utf8.h"
  #include "userdiff.h"
  #include "sigchain.h"
+ #include "submodule-config.h"
  #include "submodule.h"
  #include "ll-merge.h"
  #include "string-list.h"
@@@ -42,7 -43,7 +43,7 @@@ static long diff_algorithm
  
  static char diff_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
 -      GIT_COLOR_NORMAL,       /* PLAIN */
 +      GIT_COLOR_NORMAL,       /* CONTEXT */
        GIT_COLOR_BOLD,         /* METAINFO */
        GIT_COLOR_CYAN,         /* FRAGINFO */
        GIT_COLOR_RED,          /* OLD */
@@@ -54,8 -55,8 +55,8 @@@
  
  static int parse_diff_color_slot(const char *var)
  {
 -      if (!strcasecmp(var, "plain"))
 -              return DIFF_PLAIN;
 +      if (!strcasecmp(var, "context") || !strcasecmp(var, "plain"))
 +              return DIFF_CONTEXT;
        if (!strcasecmp(var, "meta"))
                return DIFF_METAINFO;
        if (!strcasecmp(var, "frag"))
@@@ -478,63 -479,30 +479,63 @@@ static int new_blank_line_at_eof(struc
        return ws_blank_line(line, len, ecbdata->ws_rule);
  }
  
 -static void emit_add_line(const char *reset,
 -                        struct emit_callback *ecbdata,
 -                        const char *line, int len)
 +static void emit_line_checked(const char *reset,
 +                            struct emit_callback *ecbdata,
 +                            const char *line, int len,
 +                            enum color_diff color,
 +                            unsigned ws_error_highlight,
 +                            char sign)
  {
 -      const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
 -      const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
 +      const char *set = diff_get_color(ecbdata->color_diff, color);
 +      const char *ws = NULL;
  
 -      if (!*ws)
 -              emit_line_0(ecbdata->opt, set, reset, '+', line, len);
 -      else if (new_blank_line_at_eof(ecbdata, line, len))
 +      if (ecbdata->opt->ws_error_highlight & ws_error_highlight) {
 +              ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
 +              if (!*ws)
 +                      ws = NULL;
 +      }
 +
 +      if (!ws)
 +              emit_line_0(ecbdata->opt, set, reset, sign, line, len);
 +      else if (sign == '+' && new_blank_line_at_eof(ecbdata, line, len))
                /* Blank line at EOF - paint '+' as well */
 -              emit_line_0(ecbdata->opt, ws, reset, '+', line, len);
 +              emit_line_0(ecbdata->opt, ws, reset, sign, line, len);
        else {
                /* Emit just the prefix, then the rest. */
 -              emit_line_0(ecbdata->opt, set, reset, '+', "", 0);
 +              emit_line_0(ecbdata->opt, set, reset, sign, "", 0);
                ws_check_emit(line, len, ecbdata->ws_rule,
                              ecbdata->opt->file, set, reset, ws);
        }
  }
  
 +static void emit_add_line(const char *reset,
 +                        struct emit_callback *ecbdata,
 +                        const char *line, int len)
 +{
 +      emit_line_checked(reset, ecbdata, line, len,
 +                        DIFF_FILE_NEW, WSEH_NEW, '+');
 +}
 +
 +static void emit_del_line(const char *reset,
 +                        struct emit_callback *ecbdata,
 +                        const char *line, int len)
 +{
 +      emit_line_checked(reset, ecbdata, line, len,
 +                        DIFF_FILE_OLD, WSEH_OLD, '-');
 +}
 +
 +static void emit_context_line(const char *reset,
 +                            struct emit_callback *ecbdata,
 +                            const char *line, int len)
 +{
 +      emit_line_checked(reset, ecbdata, line, len,
 +                        DIFF_CONTEXT, WSEH_CONTEXT, ' ');
 +}
 +
  static void emit_hunk_header(struct emit_callback *ecbdata,
                             const char *line, int len)
  {
 -      const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
 +      const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
        const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
        const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
        if (len < 10 ||
            memcmp(line, atat, 2) ||
            !(ep = memmem(line + 2, len - 2, atat, 2))) {
 -              emit_line(ecbdata->opt, plain, reset, line, len);
 +              emit_line(ecbdata->opt, context, reset, line, len);
                return;
        }
        ep += 2; /* skip over @@ */
                if (*ep != ' ' && *ep != '\t')
                        break;
        if (ep != cp) {
 -              strbuf_addstr(&msgbuf, plain);
 +              strbuf_addstr(&msgbuf, context);
                strbuf_add(&msgbuf, cp, ep - cp);
                strbuf_addstr(&msgbuf, reset);
        }
@@@ -636,6 -604,7 +637,6 @@@ static void emit_rewrite_lines(struct e
  {
        const char *endp = NULL;
        static const char *nneof = " No newline at end of file\n";
 -      const char *old = diff_get_color(ecb->color_diff, DIFF_FILE_OLD);
        const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
  
        while (0 < size) {
                len = endp ? (endp - data + 1) : size;
                if (prefix != '+') {
                        ecb->lno_in_preimage++;
 -                      emit_line_0(ecb->opt, old, reset, '-',
 -                                  data, len);
 +                      emit_del_line(reset, ecb, data, len);
                } else {
                        ecb->lno_in_postimage++;
                        emit_add_line(reset, ecb, data, len);
                data += len;
        }
        if (!endp) {
 -              const char *plain = diff_get_color(ecb->color_diff,
 -                                                 DIFF_PLAIN);
 +              const char *context = diff_get_color(ecb->color_diff,
 +                                                   DIFF_CONTEXT);
                putc('\n', ecb->opt->file);
 -              emit_line_0(ecb->opt, plain, reset, '\\',
 +              emit_line_0(ecb->opt, context, reset, '\\',
                            nneof, strlen(nneof));
        }
  }
@@@ -1117,7 -1087,7 +1118,7 @@@ static void init_diff_words_data(struc
                struct diff_words_style *st = ecbdata->diff_words->style;
                st->old.color = diff_get_color_opt(o, DIFF_FILE_OLD);
                st->new.color = diff_get_color_opt(o, DIFF_FILE_NEW);
 -              st->ctx.color = diff_get_color_opt(o, DIFF_PLAIN);
 +              st->ctx.color = diff_get_color_opt(o, DIFF_CONTEXT);
        }
  }
  
@@@ -1193,7 -1163,7 +1194,7 @@@ static void fn_out_consume(void *priv, 
  {
        struct emit_callback *ecbdata = priv;
        const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
 -      const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
 +      const char *context = diff_get_color(ecbdata->color_diff, DIFF_CONTEXT);
        const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
        struct diff_options *o = ecbdata->opt;
        const char *line_prefix = diff_line_prefix(o);
                }
                diff_words_flush(ecbdata);
                if (ecbdata->diff_words->type == DIFF_WORDS_PORCELAIN) {
 -                      emit_line(ecbdata->opt, plain, reset, line, len);
 +                      emit_line(ecbdata->opt, context, reset, line, len);
                        fputs("~\n", ecbdata->opt->file);
                } else {
                        /*
                              line++;
                              len--;
                        }
 -                      emit_line(ecbdata->opt, plain, reset, line, len);
 +                      emit_line(ecbdata->opt, context, reset, line, len);
                }
                return;
        }
  
 -      if (line[0] != '+') {
 -              const char *color =
 -                      diff_get_color(ecbdata->color_diff,
 -                                     line[0] == '-' ? DIFF_FILE_OLD : DIFF_PLAIN);
 -              ecbdata->lno_in_preimage++;
 -              if (line[0] == ' ')
 -                      ecbdata->lno_in_postimage++;
 -              emit_line(ecbdata->opt, color, reset, line, len);
 -      } else {
 +      switch (line[0]) {
 +      case '+':
                ecbdata->lno_in_postimage++;
                emit_add_line(reset, ecbdata, line + 1, len - 1);
 +              break;
 +      case '-':
 +              ecbdata->lno_in_preimage++;
 +              emit_del_line(reset, ecbdata, line + 1, len - 1);
 +              break;
 +      case ' ':
 +              ecbdata->lno_in_postimage++;
 +              ecbdata->lno_in_preimage++;
 +              emit_context_line(reset, ecbdata, line + 1, len - 1);
 +              break;
 +      default:
 +              /* incomplete line at the end */
 +              ecbdata->lno_in_preimage++;
 +              emit_line(ecbdata->opt,
 +                        diff_get_color(ecbdata->color_diff, DIFF_CONTEXT),
 +                        reset, line, len);
 +              break;
        }
  }
  
@@@ -3264,7 -3224,6 +3265,7 @@@ void diff_setup(struct diff_options *op
        options->rename_limit = -1;
        options->dirstat_permille = diff_dirstat_permille_default;
        options->context = diff_context_default;
 +      options->ws_error_highlight = WSEH_NEW;
        DIFF_OPT_SET(options, RENAME_EMPTY);
  
        /* pathchange left =NULL by default */
@@@ -3651,45 -3610,6 +3652,45 @@@ static void enable_patch_output(int *fm
        *fmt |= DIFF_FORMAT_PATCH;
  }
  
 +static int parse_one_token(const char **arg, const char *token)
 +{
 +      const char *rest;
 +      if (skip_prefix(*arg, token, &rest) && (!*rest || *rest == ',')) {
 +              *arg = rest;
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +static int parse_ws_error_highlight(struct diff_options *opt, const char *arg)
 +{
 +      const char *orig_arg = arg;
 +      unsigned val = 0;
 +      while (*arg) {
 +              if (parse_one_token(&arg, "none"))
 +                      val = 0;
 +              else if (parse_one_token(&arg, "default"))
 +                      val = WSEH_NEW;
 +              else if (parse_one_token(&arg, "all"))
 +                      val = WSEH_NEW | WSEH_OLD | WSEH_CONTEXT;
 +              else if (parse_one_token(&arg, "new"))
 +                      val |= WSEH_NEW;
 +              else if (parse_one_token(&arg, "old"))
 +                      val |= WSEH_OLD;
 +              else if (parse_one_token(&arg, "context"))
 +                      val |= WSEH_CONTEXT;
 +              else {
 +                      error("unknown value after ws-error-highlight=%.*s",
 +                            (int)(arg - orig_arg), orig_arg);
 +                      return 0;
 +              }
 +              if (*arg)
 +                      arg++;
 +      }
 +      opt->ws_error_highlight = val;
 +      return 1;
 +}
 +
  int diff_opt_parse(struct diff_options *options, const char **av, int ac)
  {
        const char *arg = av[0];
                DIFF_OPT_SET(options, FIND_COPIES_HARDER);
        else if (!strcmp(arg, "--follow"))
                DIFF_OPT_SET(options, FOLLOW_RENAMES);
 -      else if (!strcmp(arg, "--no-follow"))
 +      else if (!strcmp(arg, "--no-follow")) {
                DIFF_OPT_CLR(options, FOLLOW_RENAMES);
 -      else if (!strcmp(arg, "--color"))
 +              DIFF_OPT_CLR(options, DEFAULT_FOLLOW_RENAMES);
 +      } else if (!strcmp(arg, "--color"))
                options->use_color = 1;
        else if (skip_prefix(arg, "--color=", &arg)) {
                int value = git_config_colorbool(NULL, arg);
                DIFF_OPT_SET(options, SUBMODULE_LOG);
        else if (skip_prefix(arg, "--submodule=", &arg))
                return parse_submodule_opt(options, arg);
 +      else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
 +              return parse_ws_error_highlight(options, arg);
  
        /* misc options */
        else if (!strcmp(arg, "-z"))
diff --combined submodule.c
index 700bbf4fcb4cd93b79304d934bed8901c63a853a,48225595ef9f40a0fa848497c39d3b621c81afbb..9fcc86faa2ca48223eeaeaabc334d3f94d771c92
@@@ -1,4 -1,5 +1,5 @@@
  #include "cache.h"
+ #include "submodule-config.h"
  #include "submodule.h"
  #include "dir.h"
  #include "diff.h"
@@@ -12,9 -13,6 +13,6 @@@
  #include "argv-array.h"
  #include "blob.h"
  
- static struct string_list config_name_for_path;
- static struct string_list config_fetch_recurse_submodules_for_name;
- static struct string_list config_ignore_for_name;
  static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
  static struct string_list changed_submodule_paths;
  static int initialized_fetch_ref_tips;
@@@ -41,7 -39,6 +39,6 @@@ static int gitmodules_is_unmerged
   */
  static int gitmodules_is_modified;
  
  int is_staging_gitmodules_ok(void)
  {
        return !gitmodules_is_modified;
@@@ -55,7 -52,7 +52,7 @@@
  int update_path_in_gitmodules(const char *oldpath, const char *newpath)
  {
        struct strbuf entry = STRBUF_INIT;
-       struct string_list_item *path_option;
+       const struct submodule *submodule;
  
        if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
                return -1;
        if (gitmodules_is_unmerged)
                die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
  
-       path_option = unsorted_string_list_lookup(&config_name_for_path, oldpath);
-       if (!path_option) {
+       submodule = submodule_from_path(null_sha1, oldpath);
+       if (!submodule || !submodule->name) {
                warning(_("Could not find section in .gitmodules where path=%s"), oldpath);
                return -1;
        }
        strbuf_addstr(&entry, "submodule.");
-       strbuf_addstr(&entry, path_option->util);
+       strbuf_addstr(&entry, submodule->name);
        strbuf_addstr(&entry, ".path");
        if (git_config_set_in_file(".gitmodules", entry.buf, newpath) < 0) {
                /* Maybe the user already did that, don't error out here */
@@@ -89,7 -86,7 +86,7 @@@
  int remove_path_from_gitmodules(const char *path)
  {
        struct strbuf sect = STRBUF_INIT;
-       struct string_list_item *path_option;
+       const struct submodule *submodule;
  
        if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
                return -1;
        if (gitmodules_is_unmerged)
                die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
  
-       path_option = unsorted_string_list_lookup(&config_name_for_path, path);
-       if (!path_option) {
+       submodule = submodule_from_path(null_sha1, path);
+       if (!submodule || !submodule->name) {
                warning(_("Could not find section in .gitmodules where path=%s"), path);
                return -1;
        }
        strbuf_addstr(&sect, "submodule.");
-       strbuf_addstr(&sect, path_option->util);
+       strbuf_addstr(&sect, submodule->name);
        if (git_config_rename_section_in_file(".gitmodules", sect.buf, NULL) < 0) {
                /* Maybe the user already did that, don't error out here */
                warning(_("Could not remove .gitmodules entry for %s"), path);
@@@ -165,12 -162,10 +162,10 @@@ done
  void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
                                             const char *path)
  {
-       struct string_list_item *path_option, *ignore_option;
-       path_option = unsorted_string_list_lookup(&config_name_for_path, path);
-       if (path_option) {
-               ignore_option = unsorted_string_list_lookup(&config_ignore_for_name, path_option->util);
-               if (ignore_option)
-                       handle_ignore_submodules_arg(diffopt, ignore_option->util);
+       const struct submodule *submodule = submodule_from_path(null_sha1, path);
+       if (submodule) {
+               if (submodule->ignore)
+                       handle_ignore_submodules_arg(diffopt, submodule->ignore);
                else if (gitmodules_is_unmerged)
                        DIFF_OPT_SET(diffopt, IGNORE_SUBMODULES);
        }
@@@ -219,58 -214,6 +214,6 @@@ void gitmodules_config(void
        }
  }
  
- int parse_submodule_config_option(const char *var, const char *value)
- {
-       struct string_list_item *config;
-       const char *name, *key;
-       int namelen;
-       if (parse_config_key(var, "submodule", &name, &namelen, &key) < 0 || !name)
-               return 0;
-       if (!strcmp(key, "path")) {
-               if (!value)
-                       return config_error_nonbool(var);
-               config = unsorted_string_list_lookup(&config_name_for_path, value);
-               if (config)
-                       free(config->util);
-               else
-                       config = string_list_append(&config_name_for_path, xstrdup(value));
-               config->util = xmemdupz(name, namelen);
-       } else if (!strcmp(key, "fetchrecursesubmodules")) {
-               char *name_cstr = xmemdupz(name, namelen);
-               config = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name_cstr);
-               if (!config)
-                       config = string_list_append(&config_fetch_recurse_submodules_for_name, name_cstr);
-               else
-                       free(name_cstr);
-               config->util = (void *)(intptr_t)parse_fetch_recurse_submodules_arg(var, value);
-       } else if (!strcmp(key, "ignore")) {
-               char *name_cstr;
-               if (!value)
-                       return config_error_nonbool(var);
-               if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
-                   strcmp(value, "all") && strcmp(value, "none")) {
-                       warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
-                       return 0;
-               }
-               name_cstr = xmemdupz(name, namelen);
-               config = unsorted_string_list_lookup(&config_ignore_for_name, name_cstr);
-               if (config) {
-                       free(config->util);
-                       free(name_cstr);
-               } else
-                       config = string_list_append(&config_ignore_for_name, name_cstr);
-               config->util = xstrdup(value);
-               return 0;
-       }
-       return 0;
- }
  void handle_ignore_submodules_arg(struct diff_options *diffopt,
                                  const char *arg)
  {
@@@ -345,20 -288,6 +288,6 @@@ static void print_submodule_summary(str
        strbuf_release(&sb);
  }
  
- int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
- {
-       switch (git_config_maybe_bool(opt, arg)) {
-       case 1:
-               return RECURSE_SUBMODULES_ON;
-       case 0:
-               return RECURSE_SUBMODULES_OFF;
-       default:
-               if (!strcmp(arg, "on-demand"))
-                       return RECURSE_SUBMODULES_ON_DEMAND;
-               die("bad %s argument: %s", opt, arg);
-       }
- }
  void show_submodule_summary(FILE *f, const char *path,
                const char *line_prefix,
                unsigned char one[20], unsigned char two[20],
@@@ -422,8 -351,7 +351,8 @@@ void set_config_fetch_recurse_submodule
        config_fetch_recurse_submodules = value;
  }
  
 -static int has_remote(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
 +static int has_remote(const char *refname, const struct object_id *oid,
 +                    int flags, void *cb_data)
  {
        return 1;
  }
@@@ -617,10 -545,10 +546,10 @@@ static void submodule_collect_changed_c
        }
  }
  
 -static int add_sha1_to_array(const char *ref, const unsigned char *sha1,
 +static int add_sha1_to_array(const char *ref, const struct object_id *oid,
                             int flags, void *data)
  {
 -      sha1_array_append(data, sha1);
 +      sha1_array_append(data, oid->hash);
        return 0;
  }
  
@@@ -646,7 -574,7 +575,7 @@@ static void calculate_changed_submodule
        struct argv_array argv = ARGV_ARRAY_INIT;
  
        /* No need to check if there are no submodules configured */
-       if (!config_name_for_path.nr)
+       if (!submodule_from_path(NULL, NULL))
                return;
  
        init_revisions(&rev, NULL);
@@@ -693,7 -621,6 +622,6 @@@ int fetch_populated_submodules(const st
        int i, result = 0;
        struct child_process cp = CHILD_PROCESS_INIT;
        struct argv_array argv = ARGV_ARRAY_INIT;
-       struct string_list_item *name_for_path;
        const char *work_tree = get_git_work_tree();
        if (!work_tree)
                goto out;
                struct strbuf submodule_git_dir = STRBUF_INIT;
                struct strbuf submodule_prefix = STRBUF_INIT;
                const struct cache_entry *ce = active_cache[i];
-               const char *git_dir, *name, *default_argv;
+               const char *git_dir, *default_argv;
+               const struct submodule *submodule;
  
                if (!S_ISGITLINK(ce->ce_mode))
                        continue;
  
-               name = ce->name;
-               name_for_path = unsorted_string_list_lookup(&config_name_for_path, ce->name);
-               if (name_for_path)
-                       name = name_for_path->util;
+               submodule = submodule_from_path(null_sha1, ce->name);
+               if (!submodule)
+                       submodule = submodule_from_name(null_sha1, ce->name);
  
                default_argv = "yes";
                if (command_line_option == RECURSE_SUBMODULES_DEFAULT) {
-                       struct string_list_item *fetch_recurse_submodules_option;
-                       fetch_recurse_submodules_option = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name);
-                       if (fetch_recurse_submodules_option) {
-                               if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_OFF)
+                       if (submodule &&
+                           submodule->fetch_recurse !=
+                                               RECURSE_SUBMODULES_NONE) {
+                               if (submodule->fetch_recurse ==
+                                               RECURSE_SUBMODULES_OFF)
                                        continue;
-                               if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_ON_DEMAND) {
+                               if (submodule->fetch_recurse ==
+                                               RECURSE_SUBMODULES_ON_DEMAND) {
                                        if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
                                                continue;
                                        default_argv = "on-demand";
@@@ -993,7 -922,7 +923,7 @@@ static void print_commit(struct commit 
  {
        struct strbuf sb = STRBUF_INIT;
        struct pretty_print_context ctx = {0};
 -      ctx.date_mode = DATE_NORMAL;
 +      ctx.date_mode.type = DATE_NORMAL;
        format_commit_message(commit, " %h: %m %s", &sb, &ctx);
        fprintf(stderr, "%s\n", sb.buf);
        strbuf_release(&sb);