Merge branch 'ao/submodule-wo-gitmodules-checked-out'
authorJunio C Hamano <gitster@pobox.com>
Tue, 13 Nov 2018 13:37:22 +0000 (22:37 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 13 Nov 2018 13:37:22 +0000 (22:37 +0900)
The submodule support has been updated to read from the blob at
HEAD:.gitmodules when the .gitmodules file is missing from the
working tree.

* ao/submodule-wo-gitmodules-checked-out:
t/helper: add test-submodule-nested-repo-config
submodule: support reading .gitmodules when it's not in the working tree
submodule: add a helper to check if it is safe to write to .gitmodules
t7506: clean up .gitmodules properly before setting up new scenario
submodule: use the 'submodule--helper config' command
submodule--helper: add a new 'config' subcommand
t7411: be nicer to future tests and really clean things up
t7411: merge tests 5 and 6
submodule: factor out a config_set_in_gitmodules_file_gently function
submodule: add a print_config_from_gitmodules() helper

1  2 
Makefile
builtin/grep.c
builtin/submodule--helper.c
cache.h
git-submodule.sh
submodule-config.c
submodule.c
submodule.h
t/helper/test-tool.c
t/helper/test-tool.h
diff --combined Makefile
index f77bf1759769dc5a5ac3205792479d1f36506ebd,cd67f202ba3dece0a35eb2fefa96e4f633a539ac..016fdcdb817c00b2bced052a92c8a421bfef0e73
+++ b/Makefile
@@@ -400,7 -400,7 +400,7 @@@ all:
  # (defaults to "man") if you want to have a different default when
  # "git help" is called without a parameter specifying the format.
  #
 -# Define TEST_GIT_INDEX_VERSION to 2, 3 or 4 to run the test suite
 +# Define GIT_TEST_INDEX_VERSION to 2, 3 or 4 to run the test suite
  # with a different indexfile format version.  If it isn't set the index
  # file format used is index-v[23].
  #
@@@ -590,8 -590,6 +590,8 @@@ XDIFF_OBJS 
  VCSSVN_OBJS =
  GENERATED_H =
  EXTRA_CPPFLAGS =
 +FUZZ_OBJS =
 +FUZZ_PROGRAMS =
  LIB_OBJS =
  PROGRAM_OBJS =
  PROGRAMS =
@@@ -616,7 -614,7 +616,7 @@@ SCRIPT_SH += git-merge-one-file.s
  SCRIPT_SH += git-merge-resolve.sh
  SCRIPT_SH += git-mergetool.sh
  SCRIPT_SH += git-quiltimport.sh
 -SCRIPT_SH += git-rebase.sh
 +SCRIPT_SH += git-legacy-rebase.sh
  SCRIPT_SH += git-remote-testgit.sh
  SCRIPT_SH += git-request-pull.sh
  SCRIPT_SH += git-stash.sh
@@@ -626,7 -624,7 +626,7 @@@ SCRIPT_SH += git-web--browse.s
  SCRIPT_LIB += git-mergetool--lib
  SCRIPT_LIB += git-parse-remote
  SCRIPT_LIB += git-rebase--am
 -SCRIPT_LIB += git-rebase--interactive
 +SCRIPT_LIB += git-rebase--common
  SCRIPT_LIB += git-rebase--preserve-merges
  SCRIPT_LIB += git-rebase--merge
  SCRIPT_LIB += git-sh-setup
@@@ -684,14 -682,6 +684,14 @@@ SCRIPTS = $(SCRIPT_SH_INS) 
  
  ETAGS_TARGET = TAGS
  
 +FUZZ_OBJS += fuzz-pack-headers.o
 +FUZZ_OBJS += fuzz-pack-idx.o
 +
 +# Always build fuzz objects even if not testing, to prevent bit-rot.
 +all:: $(FUZZ_OBJS)
 +
 +FUZZ_PROGRAMS += $(patsubst %.o,%,$(FUZZ_OBJS))
 +
  # Empty...
  EXTRA_PROGRAMS =
  
@@@ -719,9 -709,7 +719,9 @@@ TEST_BUILTINS_OBJS += test-date.
  TEST_BUILTINS_OBJS += test-delta.o
  TEST_BUILTINS_OBJS += test-drop-caches.o
  TEST_BUILTINS_OBJS += test-dump-cache-tree.o
 +TEST_BUILTINS_OBJS += test-dump-fsmonitor.o
  TEST_BUILTINS_OBJS += test-dump-split-index.o
 +TEST_BUILTINS_OBJS += test-dump-untracked-cache.o
  TEST_BUILTINS_OBJS += test-example-decorate.o
  TEST_BUILTINS_OBJS += test-genrandom.o
  TEST_BUILTINS_OBJS += test-hashmap.o
@@@ -732,35 -720,33 +732,36 @@@ TEST_BUILTINS_OBJS += test-match-trees.
  TEST_BUILTINS_OBJS += test-mergesort.o
  TEST_BUILTINS_OBJS += test-mktemp.o
  TEST_BUILTINS_OBJS += test-online-cpus.o
 +TEST_BUILTINS_OBJS += test-parse-options.o
  TEST_BUILTINS_OBJS += test-path-utils.o
 +TEST_BUILTINS_OBJS += test-pkt-line.o
  TEST_BUILTINS_OBJS += test-prio-queue.o
 +TEST_BUILTINS_OBJS += test-reach.o
  TEST_BUILTINS_OBJS += test-read-cache.o
 +TEST_BUILTINS_OBJS += test-read-midx.o
  TEST_BUILTINS_OBJS += test-ref-store.o
  TEST_BUILTINS_OBJS += test-regex.o
  TEST_BUILTINS_OBJS += test-repository.o
  TEST_BUILTINS_OBJS += test-revision-walking.o
  TEST_BUILTINS_OBJS += test-run-command.o
  TEST_BUILTINS_OBJS += test-scrap-cache-tree.o
 -TEST_BUILTINS_OBJS += test-sha1-array.o
  TEST_BUILTINS_OBJS += test-sha1.o
 +TEST_BUILTINS_OBJS += test-sha1-array.o
  TEST_BUILTINS_OBJS += test-sigchain.o
  TEST_BUILTINS_OBJS += test-strcmp-offset.o
  TEST_BUILTINS_OBJS += test-string-list.o
  TEST_BUILTINS_OBJS += test-submodule-config.o
+ TEST_BUILTINS_OBJS += test-submodule-nested-repo-config.o
  TEST_BUILTINS_OBJS += test-subprocess.o
  TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
  TEST_BUILTINS_OBJS += test-wildmatch.o
 +TEST_BUILTINS_OBJS += test-windows-named-pipe.o
  TEST_BUILTINS_OBJS += test-write-cache.o
  
 -TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
 -TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
 +# Do not add more tests here unless they have extra dependencies. Add
 +# them in TEST_BUILTINS_OBJS above.
  TEST_PROGRAMS_NEED_X += test-fake-ssh
  TEST_PROGRAMS_NEED_X += test-line-buffer
 -TEST_PROGRAMS_NEED_X += test-parse-options
 -TEST_PROGRAMS_NEED_X += test-pkt-line
  TEST_PROGRAMS_NEED_X += test-svn-fe
  TEST_PROGRAMS_NEED_X += test-tool
  
@@@ -850,7 -836,6 +851,7 @@@ LIB_OBJS += column.
  LIB_OBJS += combine-diff.o
  LIB_OBJS += commit.o
  LIB_OBJS += commit-graph.o
 +LIB_OBJS += commit-reach.o
  LIB_OBJS += compat/obstack.o
  LIB_OBJS += compat/terminal.o
  LIB_OBJS += config.o
@@@ -863,7 -848,6 +864,7 @@@ LIB_OBJS += csum-file.
  LIB_OBJS += ctype.o
  LIB_OBJS += date.o
  LIB_OBJS += decorate.o
 +LIB_OBJS += delta-islands.o
  LIB_OBJS += diffcore-break.o
  LIB_OBJS += diffcore-delta.o
  LIB_OBJS += diffcore-order.o
@@@ -897,7 -881,6 +898,7 @@@ LIB_OBJS += linear-assignment.
  LIB_OBJS += help.o
  LIB_OBJS += hex.o
  LIB_OBJS += ident.o
 +LIB_OBJS += interdiff.o
  LIB_OBJS += json-writer.o
  LIB_OBJS += kwset.o
  LIB_OBJS += levenshtein.o
@@@ -918,7 -901,6 +919,7 @@@ LIB_OBJS += merge.
  LIB_OBJS += merge-blobs.o
  LIB_OBJS += merge-recursive.o
  LIB_OBJS += mergesort.o
 +LIB_OBJS += midx.o
  LIB_OBJS += name-hash.o
  LIB_OBJS += negotiator/default.o
  LIB_OBJS += negotiator/skipping.o
@@@ -954,7 -936,6 +955,7 @@@ LIB_OBJS += quote.
  LIB_OBJS += range-diff.o
  LIB_OBJS += reachable.o
  LIB_OBJS += read-cache.o
 +LIB_OBJS += rebase-interactive.o
  LIB_OBJS += reflog-walk.o
  LIB_OBJS += refs.o
  LIB_OBJS += refs/files-backend.o
@@@ -1080,7 -1061,6 +1081,7 @@@ BUILTIN_OBJS += builtin/merge-recursive
  BUILTIN_OBJS += builtin/merge-tree.o
  BUILTIN_OBJS += builtin/mktag.o
  BUILTIN_OBJS += builtin/mktree.o
 +BUILTIN_OBJS += builtin/multi-pack-index.o
  BUILTIN_OBJS += builtin/mv.o
  BUILTIN_OBJS += builtin/name-rev.o
  BUILTIN_OBJS += builtin/notes.o
@@@ -1094,8 -1074,7 +1095,8 @@@ BUILTIN_OBJS += builtin/pull.
  BUILTIN_OBJS += builtin/push.o
  BUILTIN_OBJS += builtin/range-diff.o
  BUILTIN_OBJS += builtin/read-tree.o
 -BUILTIN_OBJS += builtin/rebase--helper.o
 +BUILTIN_OBJS += builtin/rebase.o
 +BUILTIN_OBJS += builtin/rebase--interactive.o
  BUILTIN_OBJS += builtin/receive-pack.o
  BUILTIN_OBJS += builtin/reflog.o
  BUILTIN_OBJS += builtin/remote.o
@@@ -1808,7 -1787,6 +1809,7 @@@ ifndef 
        QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
        QUIET_GCOV     = @echo '   ' GCOV $@;
        QUIET_SP       = @echo '   ' SP $<;
 +      QUIET_HDR      = @echo '   ' HDR $<;
        QUIET_RC       = @echo '   ' RC $@;
        QUIET_SUBDIR0  = +@subdir=
        QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
@@@ -2070,7 -2048,7 +2071,7 @@@ $(BUILT_INS): git$
  
  command-list.h: generate-cmdlist.sh command-list.txt
  
 -command-list.h: $(wildcard Documentation/git*.txt) Documentation/*config.txt
 +command-list.h: $(wildcard Documentation/git*.txt) Documentation/*config.txt Documentation/config/*.txt
        $(QUIET_GEN)$(SHELL_PATH) ./generate-cmdlist.sh command-list.txt >$@+ && mv $@+ $@
  
  SCRIPT_DEFINES = $(SHELL_PATH_SQ):$(DIFF_SQ):$(GIT_VERSION):\
@@@ -2265,7 -2243,6 +2266,7 @@@ TEST_OBJS := $(patsubst %$X,%.o,$(TEST_
  OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
        $(XDIFF_OBJS) \
        $(VCSSVN_OBJS) \
 +      $(FUZZ_OBJS) \
        common-main.o \
        git.o
  ifndef NO_CURL
@@@ -2436,6 -2413,7 +2437,6 @@@ XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS
  LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
  LOCALIZED_SH = $(SCRIPT_SH)
  LOCALIZED_SH += git-parse-remote.sh
 -LOCALIZED_SH += git-rebase--interactive.sh
  LOCALIZED_SH += git-rebase--preserve-merges.sh
  LOCALIZED_SH += git-sh-setup.sh
  LOCALIZED_PERL = $(SCRIPT_PERL)
@@@ -2622,8 -2600,8 +2623,8 @@@ endi
  ifdef GIT_INTEROP_MAKE_OPTS
        @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+
  endif
 -ifdef TEST_GIT_INDEX_VERSION
 -      @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+
 +ifdef GIT_TEST_INDEX_VERSION
 +      @echo GIT_TEST_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_INDEX_VERSION)))'\' >>$@+
  endif
        @if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
  
@@@ -2691,17 -2669,6 +2692,17 @@@ $(SP_OBJ): %.sp: %.c GIT-CFLAGS FORC
  .PHONY: sparse $(SP_OBJ)
  sparse: $(SP_OBJ)
  
 +GEN_HDRS := command-list.h unicode-width.h
 +EXCEPT_HDRS := $(GEN_HDRS) compat% xdiff%
 +CHK_HDRS = $(filter-out $(EXCEPT_HDRS),$(patsubst ./%,%,$(LIB_H)))
 +HCO = $(patsubst %.h,%.hco,$(CHK_HDRS))
 +
 +$(HCO): %.hco: %.h FORCE
 +      $(QUIET_HDR)$(CC) -include git-compat-util.h -I. -o /dev/null -c -xc $<
 +
 +.PHONY: hdr-check $(HCO)
 +hdr-check: $(HCO)
 +
  .PHONY: style
  style:
        git clang-format --style file --diff --extensions c,h
@@@ -2963,7 -2930,6 +2964,7 @@@ clean: profile-clean coverage-clean coc
        $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
 +      $(RM) $(FUZZ_PROGRAMS)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
        $(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope*
@@@ -3088,24 -3054,3 +3089,24 @@@ cover_db: coverage-repor
  cover_db_html: cover_db
        cover -report html -outputdir cover_db_html cover_db
  
 +
 +### Fuzz testing
 +#
 +# Building fuzz targets generally requires a special set of compiler flags that
 +# are not necessarily appropriate for general builds, and that vary greatly
 +# depending on the compiler version used.
 +#
 +# An example command to build against libFuzzer from LLVM 4.0.0:
 +#
 +# make CC=clang CXX=clang++ \
 +#      CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
 +#      LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \
 +#      fuzz-all
 +#
 +.PHONY: fuzz-all
 +
 +$(FUZZ_PROGRAMS): all
 +      $(QUIET_LINK)$(CXX) $(CFLAGS) $(LIB_OBJS) $(BUILTIN_OBJS) \
 +              $(XDIFF_OBJS) $(EXTLIBS) git.o $@.o $(LIB_FUZZING_ENGINE) -o $@
 +
 +fuzz-all: $(FUZZ_PROGRAMS)
diff --combined builtin/grep.c
index d8508ddf792dd22dc0c8c283df29e5e6d9dacbf7,7da8fef31aa916bd53642d96697e78ac50926789..56e4a110526379d1df69615a0ade611c2a82c17d
@@@ -103,8 -103,7 +103,8 @@@ static void add_work(struct grep_opt *o
  
        todo[todo_end].source = *gs;
        if (opt->binary != GREP_BINARY_TEXT)
 -              grep_source_load_driver(&todo[todo_end].source);
 +              grep_source_load_driver(&todo[todo_end].source,
 +                                      opt->repo->index);
        todo[todo_end].done = 0;
        strbuf_reset(&todo[todo_end].out);
        todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
@@@ -422,11 -421,23 +422,23 @@@ static int grep_submodule(struct grep_o
        struct repository submodule;
        int hit;
  
-       if (!is_submodule_active(superproject, path))
+       /*
+        * NEEDSWORK: submodules functions need to be protected because they
+        * access the object store via config_from_gitmodules(): the latter
+        * uses get_oid() which, for now, relies on the global the_repository
+        * object.
+        */
+       grep_read_lock();
+       if (!is_submodule_active(superproject, path)) {
+               grep_read_unlock();
                return 0;
+       }
  
-       if (repo_submodule_init(&submodule, superproject, path))
+       if (repo_submodule_init(&submodule, superproject, path)) {
+               grep_read_unlock();
                return 0;
+       }
  
        repo_read_gitmodules(&submodule);
  
         * store is no longer global and instead is a member of the repository
         * object.
         */
-       grep_read_lock();
        add_to_alternates_memory(submodule.objects->objectdir);
        grep_read_unlock();
  
@@@ -812,8 -822,6 +823,8 @@@ int cmd_grep(int argc, const char **arg
                        GREP_BINARY_NOMATCH),
                OPT_BOOL(0, "textconv", &opt.allow_textconv,
                         N_("process binary files with textconv filters")),
 +              OPT_SET_INT('r', "recursive", &opt.max_depth,
 +                          N_("search in subdirectories (default)"), -1),
                { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"),
                        N_("descend at most <depth> levels"), PARSE_OPT_NONEG,
                        NULL, 1 },
                OPT_END()
        };
  
 -      init_grep_defaults();
 +      init_grep_defaults(the_repository);
        git_config(grep_cmd_config, NULL);
 -      grep_init(&opt, prefix);
 +      grep_init(&opt, the_repository, prefix);
  
        /*
         * If there is no -- then the paths must exist in the working
index 676175b9befa23c70e56e12c00746b8faf899b8c,43d2a132a9713b4bfb23828616056a76da8a76b7..d38113a31aeb3838190b7339475c7454e45f3a89
@@@ -584,26 -584,6 +584,26 @@@ static int module_foreach(int argc, con
        return 0;
  }
  
 +static char *compute_submodule_clone_url(const char *rel_url)
 +{
 +      char *remoteurl, *relurl;
 +      char *remote = get_default_remote();
 +      struct strbuf remotesb = STRBUF_INIT;
 +
 +      strbuf_addf(&remotesb, "remote.%s.url", remote);
 +      if (git_config_get_string(remotesb.buf, &remoteurl)) {
 +              warning(_("could not look up configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
 +              remoteurl = xgetcwd();
 +      }
 +      relurl = relative_url(remoteurl, rel_url, NULL);
 +
 +      free(remote);
 +      free(remoteurl);
 +      strbuf_release(&remotesb);
 +
 +      return relurl;
 +}
 +
  struct init_cb {
        const char *prefix;
        unsigned int flags;
@@@ -654,9 -634,21 +654,9 @@@ static void init_submodule(const char *
                /* Possibly a url relative to parent */
                if (starts_with_dot_dot_slash(url) ||
                    starts_with_dot_slash(url)) {
 -                      char *remoteurl, *relurl;
 -                      char *remote = get_default_remote();
 -                      struct strbuf remotesb = STRBUF_INIT;
 -                      strbuf_addf(&remotesb, "remote.%s.url", remote);
 -                      free(remote);
 -
 -                      if (git_config_get_string(remotesb.buf, &remoteurl)) {
 -                              warning(_("could not lookup configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
 -                              remoteurl = xgetcwd();
 -                      }
 -                      relurl = relative_url(remoteurl, url, NULL);
 -                      strbuf_release(&remotesb);
 -                      free(remoteurl);
 -                      free(url);
 -                      url = relurl;
 +                      char *oldurl = url;
 +                      url = compute_submodule_clone_url(oldurl);
 +                      free(oldurl);
                }
  
                if (git_config_set_gently(sb.buf, url))
@@@ -800,7 -792,7 +800,7 @@@ static void status_submodule(const cha
                         path, NULL);
  
        git_config(git_diff_basic_config, NULL);
 -      init_revisions(&rev, prefix);
 +      repo_init_revisions(the_repository, &rev, prefix);
        rev.abbrev = 0;
        diff_files_args.argc = setup_revisions(diff_files_args.argc,
                                               diff_files_args.argv,
@@@ -1241,7 -1233,6 +1241,7 @@@ static int clone_submodule(const char *
        if (gitdir && *gitdir)
                argv_array_pushl(&cp.args, "--separate-git-dir", gitdir, NULL);
  
 +      argv_array_push(&cp.args, "--");
        argv_array_push(&cp.args, url);
        argv_array_push(&cp.args, path);
  
@@@ -1452,70 -1443,6 +1452,70 @@@ static int module_clone(int argc, cons
        return 0;
  }
  
 +static void determine_submodule_update_strategy(struct repository *r,
 +                                              int just_cloned,
 +                                              const char *path,
 +                                              const char *update,
 +                                              struct submodule_update_strategy *out)
 +{
 +      const struct submodule *sub = submodule_from_path(r, &null_oid, path);
 +      char *key;
 +      const char *val;
 +
 +      key = xstrfmt("submodule.%s.update", sub->name);
 +
 +      if (update) {
 +              if (parse_submodule_update_strategy(update, out) < 0)
 +                      die(_("Invalid update mode '%s' for submodule path '%s'"),
 +                              update, path);
 +      } else if (!repo_config_get_string_const(r, key, &val)) {
 +              if (parse_submodule_update_strategy(val, out) < 0)
 +                      die(_("Invalid update mode '%s' configured for submodule path '%s'"),
 +                              val, path);
 +      } else if (sub->update_strategy.type != SM_UPDATE_UNSPECIFIED) {
 +              out->type = sub->update_strategy.type;
 +              out->command = sub->update_strategy.command;
 +      } else
 +              out->type = SM_UPDATE_CHECKOUT;
 +
 +      if (just_cloned &&
 +          (out->type == SM_UPDATE_MERGE ||
 +           out->type == SM_UPDATE_REBASE ||
 +           out->type == SM_UPDATE_NONE))
 +              out->type = SM_UPDATE_CHECKOUT;
 +
 +      free(key);
 +}
 +
 +static int module_update_module_mode(int argc, const char **argv, const char *prefix)
 +{
 +      const char *path, *update = NULL;
 +      int just_cloned;
 +      struct submodule_update_strategy update_strategy = { .type = SM_UPDATE_CHECKOUT };
 +
 +      if (argc < 3 || argc > 4)
 +              die("submodule--helper update-module-clone expects <just-cloned> <path> [<update>]");
 +
 +      just_cloned = git_config_int("just_cloned", argv[1]);
 +      path = argv[2];
 +
 +      if (argc == 4)
 +              update = argv[3];
 +
 +      determine_submodule_update_strategy(the_repository,
 +                                          just_cloned, path, update,
 +                                          &update_strategy);
 +      fputs(submodule_strategy_to_string(&update_strategy), stdout);
 +
 +      return 0;
 +}
 +
 +struct update_clone_data {
 +      const struct submodule *sub;
 +      struct object_id oid;
 +      unsigned just_cloned;
 +};
 +
  struct submodule_update_clone {
        /* index into 'list', the list of submodules to look into for cloning */
        int current;
        const char *recursive_prefix;
        const char *prefix;
  
 -      /* Machine-readable status lines to be consumed by git-submodule.sh */
 -      struct string_list projectlines;
 +      /* to be consumed by git-submodule.sh */
 +      struct update_clone_data *update_clone;
 +      int update_clone_nr; int update_clone_alloc;
  
        /* If we want to stop as fast as possible and return an error */
        unsigned quickstop : 1;
        /* failed clones to be retried again */
        const struct cache_entry **failed_clones;
        int failed_clones_nr, failed_clones_alloc;
 +
 +      int max_jobs;
  };
  #define SUBMODULE_UPDATE_CLONE_INIT {0, MODULE_LIST_INIT, 0, \
        SUBMODULE_UPDATE_STRATEGY_INIT, 0, 0, -1, STRING_LIST_INIT_DUP, 0, \
        NULL, NULL, NULL, \
 -      STRING_LIST_INIT_DUP, 0, NULL, 0, 0}
 +      NULL, 0, 0, 0, NULL, 0, 0, 0}
  
  
  static void next_submodule_warn_missing(struct submodule_update_clone *suc,
@@@ -1590,7 -1514,6 +1590,7 @@@ static int prepare_to_clone_next_submod
        struct strbuf sb = STRBUF_INIT;
        const char *displaypath = NULL;
        int needs_cloning = 0;
 +      int need_free_url = 0;
  
        if (ce_stage(ce)) {
                if (suc->recursive_prefix)
  
        strbuf_reset(&sb);
        strbuf_addf(&sb, "submodule.%s.url", sub->name);
 -      if (repo_config_get_string_const(the_repository, sb.buf, &url))
 -              url = sub->url;
 +      if (repo_config_get_string_const(the_repository, sb.buf, &url)) {
 +              if (starts_with_dot_slash(sub->url) ||
 +                  starts_with_dot_dot_slash(sub->url)) {
 +                      url = compute_submodule_clone_url(sub->url);
 +                      need_free_url = 1;
 +              } else
 +                      url = sub->url;
 +      }
  
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/.git", ce->name);
        needs_cloning = !file_exists(sb.buf);
  
 -      strbuf_reset(&sb);
 -      strbuf_addf(&sb, "%06o %s %d %d\t%s\n", ce->ce_mode,
 -                      oid_to_hex(&ce->oid), ce_stage(ce),
 -                      needs_cloning, ce->name);
 -      string_list_append(&suc->projectlines, sb.buf);
 +      ALLOC_GROW(suc->update_clone, suc->update_clone_nr + 1,
 +                 suc->update_clone_alloc);
 +      oidcpy(&suc->update_clone[suc->update_clone_nr].oid, &ce->oid);
 +      suc->update_clone[suc->update_clone_nr].just_cloned = needs_cloning;
 +      suc->update_clone[suc->update_clone_nr].sub = sub;
 +      suc->update_clone_nr++;
  
        if (!needs_cloning)
                goto cleanup;
  cleanup:
        strbuf_reset(&displaypath_sb);
        strbuf_reset(&sb);
 +      if (need_free_url)
 +              free((void*)url);
  
        return needs_cloning;
  }
@@@ -1800,44 -1714,11 +1800,44 @@@ static int git_update_clone_config(cons
        return 0;
  }
  
 +static void update_submodule(struct update_clone_data *ucd)
 +{
 +      fprintf(stdout, "dummy %s %d\t%s\n",
 +              oid_to_hex(&ucd->oid),
 +              ucd->just_cloned,
 +              ucd->sub->path);
 +}
 +
 +static int update_submodules(struct submodule_update_clone *suc)
 +{
 +      int i;
 +
 +      run_processes_parallel(suc->max_jobs,
 +                             update_clone_get_next_task,
 +                             update_clone_start_failure,
 +                             update_clone_task_finished,
 +                             suc);
 +
 +      /*
 +       * We saved the output and put it out all at once now.
 +       * That means:
 +       * - the listener does not have to interleave their (checkout)
 +       *   work with our fetching.  The writes involved in a
 +       *   checkout involve more straightforward sequential I/O.
 +       * - the listener can avoid doing any work if fetching failed.
 +       */
 +      if (suc->quickstop)
 +              return 1;
 +
 +      for (i = 0; i < suc->update_clone_nr; i++)
 +              update_submodule(&suc->update_clone[i]);
 +
 +      return 0;
 +}
 +
  static int update_clone(int argc, const char **argv, const char *prefix)
  {
        const char *update = NULL;
 -      int max_jobs = 1;
 -      struct string_list_item *item;
        struct pathspec pathspec;
        struct submodule_update_clone suc = SUBMODULE_UPDATE_CLONE_INIT;
  
                OPT_STRING(0, "depth", &suc.depth, "<depth>",
                           N_("Create a shallow clone truncated to the "
                              "specified number of revisions")),
 -              OPT_INTEGER('j', "jobs", &max_jobs,
 +              OPT_INTEGER('j', "jobs", &suc.max_jobs,
                            N_("parallel jobs")),
                OPT_BOOL(0, "recommend-shallow", &suc.recommend_shallow,
                            N_("whether the initial clone should follow the shallow recommendation")),
        };
        suc.prefix = prefix;
  
 -      update_clone_config_from_gitmodules(&max_jobs);
 -      git_config(git_update_clone_config, &max_jobs);
 +      update_clone_config_from_gitmodules(&suc.max_jobs);
 +      git_config(git_update_clone_config, &suc.max_jobs);
  
        argc = parse_options(argc, argv, prefix, module_update_clone_options,
                             git_submodule_helper_usage, 0);
        if (pathspec.nr)
                suc.warn_if_uninitialized = 1;
  
 -      run_processes_parallel(max_jobs,
 -                             update_clone_get_next_task,
 -                             update_clone_start_failure,
 -                             update_clone_task_finished,
 -                             &suc);
 -
 -      /*
 -       * We saved the output and put it out all at once now.
 -       * That means:
 -       * - the listener does not have to interleave their (checkout)
 -       *   work with our fetching.  The writes involved in a
 -       *   checkout involve more straightforward sequential I/O.
 -       * - the listener can avoid doing any work if fetching failed.
 -       */
 -      if (suc.quickstop)
 -              return 1;
 -
 -      for_each_string_list_item(item, &suc.projectlines)
 -              fprintf(stdout, "%s", item->string);
 -
 -      return 0;
 +      return update_submodules(&suc);
  }
  
  static int resolve_relative_path(int argc, const char **argv, const char *prefix)
@@@ -2037,45 -1938,6 +2037,45 @@@ static int push_check(int argc, const c
        return 0;
  }
  
 +static int ensure_core_worktree(int argc, const char **argv, const char *prefix)
 +{
 +      const struct submodule *sub;
 +      const char *path;
 +      char *cw;
 +      struct repository subrepo;
 +
 +      if (argc != 2)
 +              BUG("submodule--helper connect-gitdir-workingtree <name> <path>");
 +
 +      path = argv[1];
 +
 +      sub = submodule_from_path(the_repository, &null_oid, path);
 +      if (!sub)
 +              BUG("We could get the submodule handle before?");
 +
 +      if (repo_submodule_init(&subrepo, the_repository, path))
 +              die(_("could not get a repository handle for submodule '%s'"), path);
 +
 +      if (!repo_config_get_string(&subrepo, "core.worktree", &cw)) {
 +              char *cfg_file, *abs_path;
 +              const char *rel_path;
 +              struct strbuf sb = STRBUF_INIT;
 +
 +              cfg_file = repo_git_path(&subrepo, "config");
 +
 +              abs_path = absolute_pathdup(path);
 +              rel_path = relative_path(abs_path, subrepo.gitdir, &sb);
 +
 +              git_config_set_in_file(cfg_file, "core.worktree", rel_path);
 +
 +              free(cfg_file);
 +              free(abs_path);
 +              strbuf_release(&sb);
 +      }
 +
 +      return 0;
 +}
 +
  static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
  {
        int i;
@@@ -2141,6 -2003,45 +2141,45 @@@ static int check_name(int argc, const c
        return 0;
  }
  
+ static int module_config(int argc, const char **argv, const char *prefix)
+ {
+       enum {
+               CHECK_WRITEABLE = 1
+       } command = 0;
+       struct option module_config_options[] = {
+               OPT_CMDMODE(0, "check-writeable", &command,
+                           N_("check if it is safe to write to the .gitmodules file"),
+                           CHECK_WRITEABLE),
+               OPT_END()
+       };
+       const char *const git_submodule_helper_usage[] = {
+               N_("git submodule--helper config name [value]"),
+               N_("git submodule--helper config --check-writeable"),
+               NULL
+       };
+       argc = parse_options(argc, argv, prefix, module_config_options,
+                            git_submodule_helper_usage, PARSE_OPT_KEEP_ARGV0);
+       if (argc == 1 && command == CHECK_WRITEABLE)
+               return is_writing_gitmodules_ok() ? 0 : -1;
+       /* Equivalent to ACTION_GET in builtin/config.c */
+       if (argc == 2)
+               return print_config_from_gitmodules(the_repository, argv[1]);
+       /* Equivalent to ACTION_SET in builtin/config.c */
+       if (argc == 3) {
+               if (!is_writing_gitmodules_ok())
+                       die(_("please make sure that the .gitmodules file is in the working tree"));
+               return config_set_in_gitmodules_file_gently(argv[1], argv[2]);
+       }
+       usage_with_options(git_submodule_helper_usage, module_config_options);
+ }
  #define SUPPORT_SUPER_PREFIX (1<<0)
  
  struct cmd_struct {
@@@ -2153,9 -2054,7 +2192,9 @@@ static struct cmd_struct commands[] = 
        {"list", module_list, 0},
        {"name", module_name, 0},
        {"clone", module_clone, 0},
 +      {"update-module-mode", module_update_module_mode, 0},
        {"update-clone", update_clone, 0},
 +      {"ensure-core-worktree", ensure_core_worktree, 0},
        {"relative-path", resolve_relative_path, 0},
        {"resolve-relative-url", resolve_relative_url, 0},
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
        {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
        {"is-active", is_active, 0},
        {"check-name", check_name, 0},
+       {"config", module_config, 0},
  };
  
  int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
diff --combined cache.h
index a59141f8b2a9843d9c2c2c62c84871be6d0cbf39,33723d2a3246a305ad522c3a8cf3b24c4e2fb452..8ac4adb3644f789ef644c630a2ab1bb86941fd61
+++ b/cache.h
@@@ -410,7 -410,7 +410,7 @@@ void validate_cache_entries(const struc
  
  #define read_cache() read_index(&the_index)
  #define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir()))
 -#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
 +#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec), 0)
  #define is_cache_unborn() is_index_unborn(&the_index)
  #define read_cache_unmerged() read_index_unmerged(&the_index)
  #define discard_cache() discard_index(&the_index)
@@@ -486,6 -486,8 +486,8 @@@ static inline enum object_type object_t
  #define INFOATTRIBUTES_FILE "info/attributes"
  #define ATTRIBUTE_MACRO_PREFIX "[attr]"
  #define GITMODULES_FILE ".gitmodules"
+ #define GITMODULES_INDEX ":.gitmodules"
+ #define GITMODULES_HEAD "HEAD:.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"
@@@ -659,9 -661,7 +661,9 @@@ extern int daemonize(void)
  /* Initialize and use the cache information */
  struct lock_file;
  extern int read_index(struct index_state *);
 -extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
 +extern int read_index_preload(struct index_state *,
 +                            const struct pathspec *pathspec,
 +                            unsigned int refresh_flags);
  extern int do_read_index(struct index_state *istate, const char *path,
                         int must_exist); /* for testting only! */
  extern int read_index_from(struct index_state *, const char *path,
@@@ -705,7 -705,7 +707,7 @@@ extern int unmerged_index(const struct 
   * provided, the space-separated list of files that differ will be appended
   * to it.
   */
 -extern int index_has_changes(const struct index_state *istate,
 +extern int index_has_changes(struct index_state *istate,
                             struct tree *tree,
                             struct strbuf *sb);
  
@@@ -783,16 -783,14 +785,16 @@@ extern void *read_blob_data_from_index(
  #define CE_MATCH_REFRESH              0x10
  /* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
  #define CE_MATCH_IGNORE_FSMONITOR 0X20
 +extern int is_racy_timestamp(const struct index_state *istate,
 +                           const struct cache_entry *ce);
  extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
  #define HASH_RENORMALIZE  4
 -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);
 +extern int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
 +extern int index_path(struct index_state *istate, 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
@@@ -818,7 -816,6 +820,7 @@@ extern void fill_stat_cache_info(struc
  #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
  #define REFRESH_IGNORE_SUBMODULES     0x0010  /* ignore submodules */
  #define REFRESH_IN_PORCELAIN  0x0020  /* user friendly output, not "needs update" */
 +#define REFRESH_PROGRESS      0x0040  /* show progress bar if stderr is tty */
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
  
@@@ -906,6 -903,14 +908,6 @@@ int use_optional_locks(void)
  extern char comment_line_char;
  extern int auto_comment_line_char;
  
 -/* Windows only */
 -enum hide_dotfiles_type {
 -      HIDE_DOTFILES_FALSE = 0,
 -      HIDE_DOTFILES_TRUE,
 -      HIDE_DOTFILES_DOTGITONLY
 -};
 -extern enum hide_dotfiles_type hide_dotfiles;
 -
  enum log_refs_config {
        LOG_REFS_UNSET = -1,
        LOG_REFS_NONE = 0,
@@@ -954,13 -959,11 +956,13 @@@ extern int grafts_replace_parents
  extern int repository_format_precious_objects;
  extern char *repository_format_partial_clone;
  extern const char *core_partial_clone_filter_default;
 +extern int repository_format_worktree_config;
  
  struct repository_format {
        int version;
        int precious_objects;
        char *partial_clone; /* value of extensions.partialclone */
 +      int worktree_config;
        int is_bare;
        int hash_algo;
        char *work_tree;
@@@ -1040,24 -1043,14 +1042,24 @@@ static inline int oidcmp(const struct o
        return hashcmp(oid1->hash, oid2->hash);
  }
  
 +static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)
 +{
 +      return !hashcmp(sha1, sha2);
 +}
 +
 +static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)
 +{
 +      return hasheq(oid1->hash, oid2->hash);
 +}
 +
  static inline int is_null_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, null_sha1);
 +      return hasheq(sha1, null_sha1);
  }
  
  static inline int is_null_oid(const struct object_id *oid)
  {
 -      return !hashcmp(oid->hash, null_sha1);
 +      return hasheq(oid->hash, null_sha1);
  }
  
  static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
@@@ -1094,22 -1087,22 +1096,22 @@@ static inline void oidread(struct objec
  
  static inline int is_empty_blob_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, the_hash_algo->empty_blob->hash);
 +      return hasheq(sha1, the_hash_algo->empty_blob->hash);
  }
  
  static inline int is_empty_blob_oid(const struct object_id *oid)
  {
 -      return !oidcmp(oid, the_hash_algo->empty_blob);
 +      return oideq(oid, the_hash_algo->empty_blob);
  }
  
  static inline int is_empty_tree_sha1(const unsigned char *sha1)
  {
 -      return !hashcmp(sha1, the_hash_algo->empty_tree->hash);
 +      return hasheq(sha1, the_hash_algo->empty_tree->hash);
  }
  
  static inline int is_empty_tree_oid(const struct object_id *oid)
  {
 -      return !oidcmp(oid, the_hash_algo->empty_tree);
 +      return oideq(oid, the_hash_algo->empty_tree);
  }
  
  const char *empty_tree_oid_hex(void);
@@@ -1481,7 -1474,6 +1483,7 @@@ extern const char *fmt_name(const char 
  extern const char *ident_default_name(void);
  extern const char *ident_default_email(void);
  extern const char *git_editor(void);
 +extern const char *git_sequence_editor(void);
  extern const char *git_pager(int stdout_is_tty);
  extern int is_terminal_dumb(void);
  extern int git_ident_config(const char *, const char *, void *);
@@@ -1528,7 -1520,6 +1530,7 @@@ struct checkout 
        unsigned force:1,
                 quiet:1,
                 not_new:1,
 +               clone:1,
                 refresh_cache:1;
  };
  #define CHECKOUT_INIT { NULL, "" }
@@@ -1705,7 -1696,7 +1707,7 @@@ void shift_tree_by(const struct object_
  /* 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 whitespace_rule(struct index_state *, const char *);
  extern unsigned parse_whitespace_rule(const char *);
  extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
  extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
@@@ -1727,12 -1718,10 +1729,12 @@@ extern struct startup_info *startup_inf
  
  /* merge.c */
  struct commit_list;
 -int try_merge_command(const char *strategy, size_t xopts_nr,
 +int try_merge_command(struct repository *r,
 +              const char *strategy, size_t xopts_nr,
                const char **xopts, struct commit_list *common,
                const char *head_arg, struct commit_list *remotes);
 -int checkout_fast_forward(const struct object_id *from,
 +int checkout_fast_forward(struct repository *r,
 +                        const struct object_id *from,
                          const struct object_id *to,
                          int overwrite_ignore);
  
diff --combined git-submodule.sh
index c09eb3e03d25510b2768d8f9c144cab2597fc000,bff855f54adf9674ddcc7b8213d103d56788f9e6..5e608f8bad305fea40e9063b854bf1be24ebd448
@@@ -72,7 -72,7 +72,7 @@@ get_submodule_config () 
        value=$(git config submodule."$name"."$option")
        if test -z "$value"
        then
-               value=$(git config -f .gitmodules submodule."$name"."$option")
+               value=$(git submodule--helper config submodule."$name"."$option")
        fi
        printf '%s' "${value:-$default}"
  }
@@@ -82,11 -82,6 +82,11 @@@ isnumber(
        n=$(($1 + 0)) 2>/dev/null && test "$n" = "$1"
  }
  
 +# Given a full hex object ID, is this the zero OID?
 +is_zero_oid () {
 +      echo "$1" | sane_egrep '^0+$' >/dev/null 2>&1
 +}
 +
  # Sanitize the local git environment for use within a submodule. We
  # can't simply use clear_local_git_env since we want to preserve some
  # of the settings from GIT_CONFIG_PARAMETERS.
@@@ -164,6 -159,11 +164,11 @@@ cmd_add(
                shift
        done
  
+       if ! git submodule--helper config --check-writeable >/dev/null 2>&1
+       then
+                die "$(eval_gettext "please make sure that the .gitmodules file is in the working tree")"
+       fi
        if test -n "$reference_path"
        then
                is_absolute_path "$reference_path" ||
@@@ -288,11 -288,11 +293,11 @@@ or you are unsure what this means choos
        git add --no-warn-embedded-repo $force "$sm_path" ||
        die "$(eval_gettext "Failed to add submodule '\$sm_path'")"
  
-       git config -f .gitmodules submodule."$sm_name".path "$sm_path" &&
-       git config -f .gitmodules submodule."$sm_name".url "$repo" &&
+       git submodule--helper config submodule."$sm_name".path "$sm_path" &&
+       git submodule--helper config submodule."$sm_name".url "$repo" &&
        if test -n "$branch"
        then
-               git config -f .gitmodules submodule."$sm_name".branch "$branch"
+               git submodule--helper config submodule."$sm_name".branch "$branch"
        fi &&
        git add --force .gitmodules ||
        die "$(eval_gettext "Failed to register submodule '\$sm_path'")"
@@@ -539,19 -539,31 +544,19 @@@ cmd_update(
                "$@" || echo "#unmatched" $?
        } | {
        err=
 -      while read -r mode sha1 stage just_cloned sm_path
 +      while read -r quickabort sha1 just_cloned sm_path
        do
 -              die_if_unmatched "$mode" "$sha1"
 +              die_if_unmatched "$quickabort" "$sha1"
  
 -              name=$(git submodule--helper name "$sm_path") || exit
 -              if ! test -z "$update"
 -              then
 -                      update_module=$update
 -              else
 -                      update_module=$(git config submodule."$name".update)
 -                      if test -z "$update_module"
 -                      then
 -                              update_module="checkout"
 -                      fi
 -              fi
 +              git submodule--helper ensure-core-worktree "$sm_path"
 +
 +              update_module=$(git submodule--helper update-module-mode $just_cloned "$sm_path" $update)
  
                displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
  
                if test $just_cloned -eq 1
                then
                        subsha1=
 -                      case "$update_module" in
 -                      merge | rebase | none)
 -                              update_module=checkout ;;
 -                      esac
                else
                        subsha1=$(sanitize_submodule_env; cd "$sm_path" &&
                                git rev-parse --verify HEAD) ||
                                must_die_on_failure=yes
                                ;;
                        *)
 -                              die "$(eval_gettext "Invalid update mode '$update_module' for submodule '$name'")"
 +                              die "$(eval_gettext "Invalid update mode '$update_module' for submodule path '$path'")"
                        esac
  
                        if (sanitize_submodule_env; cd "$sm_path" && $command "$sha1")
@@@ -785,7 -797,7 +790,7 @@@ cmd_summary() 
        while read -r mod_src mod_dst sha1_src sha1_dst status name
        do
                if test -z "$cached" &&
 -                      test $sha1_dst = 0000000000000000000000000000000000000000
 +                      is_zero_oid $sha1_dst
                then
                        case "$mod_dst" in
                        160000)
diff --combined submodule-config.c
index b132f7a80ba6fc692069f765caf0b92027b920d8,6869394c32fcede018701a4f6af1da0bffb2c105..52702c62d9e3a205c14bdd9f468509f98fe4ced1
@@@ -1,4 -1,5 +1,5 @@@
  #include "cache.h"
+ #include "dir.h"
  #include "repository.h"
  #include "config.h"
  #include "submodule-config.h"
@@@ -45,7 -46,7 +46,7 @@@ static int config_path_cmp(const void *
        const struct submodule_entry *b = entry_or_key;
  
        return strcmp(a->config->path, b->config->path) ||
 -             oidcmp(&a->config->gitmodules_oid, &b->config->gitmodules_oid);
 +             !oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid);
  }
  
  static int config_name_cmp(const void *unused_cmp_data,
@@@ -57,7 -58,7 +58,7 @@@
        const struct submodule_entry *b = entry_or_key;
  
        return strcmp(a->config->name, b->config->name) ||
 -             oidcmp(&a->config->gitmodules_oid, &b->config->gitmodules_oid);
 +             !oideq(&a->config->gitmodules_oid, &b->config->gitmodules_oid);
  }
  
  static struct submodule_cache *submodule_cache_alloc(void)
@@@ -384,12 -385,6 +385,12 @@@ static void warn_multiple_config(const 
                        commit_string, name, option);
  }
  
 +static void warn_command_line_option(const char *var, const char *value)
 +{
 +      warning(_("ignoring '%s' which may be interpreted as"
 +                " a command-line option: %s"), var, value);
 +}
 +
  struct parse_config_parameter {
        struct submodule_cache *cache;
        const struct object_id *treeish_name;
@@@ -415,8 -410,6 +416,8 @@@ static int parse_config(const char *var
        if (!strcmp(item.buf, "path")) {
                if (!value)
                        ret = config_error_nonbool(var);
 +              else if (looks_like_command_line_option(value))
 +                      warn_command_line_option(var, value);
                else if (!me->overwrite && submodule->path)
                        warn_multiple_config(me->treeish_name, submodule->name,
                                        "path");
        } else if (!strcmp(item.buf, "url")) {
                if (!value) {
                        ret = config_error_nonbool(var);
 +              } else if (looks_like_command_line_option(value)) {
 +                      warn_command_line_option(var, value);
                } else if (!me->overwrite && submodule->url) {
                        warn_multiple_config(me->treeish_name, submodule->name,
                                        "url");
@@@ -613,8 -604,34 +614,34 @@@ static void submodule_cache_check_init(
  static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void *data)
  {
        if (repo->worktree) {
-               char *file = repo_worktree_path(repo, GITMODULES_FILE);
-               git_config_from_file(fn, file, data);
+               struct git_config_source config_source = { 0 };
+               const struct config_options opts = { 0 };
+               struct object_id oid;
+               char *file;
+               file = repo_worktree_path(repo, GITMODULES_FILE);
+               if (file_exists(file)) {
+                       config_source.file = file;
+               } else if (repo->submodule_prefix) {
+                       /*
+                        * When get_oid and config_with_options, used below,
+                        * become able to work on a specific repository, this
+                        * warning branch can be removed.
+                        */
+                       warning("nested submodules without %s in the working tree are not supported yet",
+                               GITMODULES_FILE);
+                       goto out;
+               } else if (get_oid(GITMODULES_INDEX, &oid) >= 0) {
+                       config_source.blob = GITMODULES_INDEX;
+               } else if (get_oid(GITMODULES_HEAD, &oid) >= 0) {
+                       config_source.blob = GITMODULES_HEAD;
+               } else {
+                       goto out;
+               }
+               config_with_options(fn, data, &config_source, &opts);
+ out:
                free(file);
        }
  }
@@@ -692,6 -709,43 +719,43 @@@ void submodule_free(struct repository *
                submodule_cache_clear(r->submodule_cache);
  }
  
+ static int config_print_callback(const char *var, const char *value, void *cb_data)
+ {
+       char *wanted_key = cb_data;
+       if (!strcmp(wanted_key, var))
+               printf("%s\n", value);
+       return 0;
+ }
+ int print_config_from_gitmodules(struct repository *repo, const char *key)
+ {
+       int ret;
+       char *store_key;
+       ret = git_config_parse_key(key, &store_key, NULL);
+       if (ret < 0)
+               return CONFIG_INVALID_KEY;
+       config_from_gitmodules(config_print_callback, repo, store_key);
+       free(store_key);
+       return 0;
+ }
+ int config_set_in_gitmodules_file_gently(const char *key, const char *value)
+ {
+       int ret;
+       ret = git_config_set_in_file_gently(GITMODULES_FILE, key, value);
+       if (ret < 0)
+               /* Maybe the user already did that, don't error out here */
+               warning(_("Could not update .gitmodules entry %s"), key);
+       return ret;
+ }
  struct fetch_config {
        int *max_children;
        int *recurse_submodules;
diff --combined submodule.c
index a2701ede4a1135fae736705e92882885d4bdce47,2b7082b2dbba735d81998dd4a31b1dbe9ab6b14f..6415cc55807c7ed9ee4cbcaa57f41a5804b6e854
@@@ -22,7 -22,6 +22,7 @@@
  #include "worktree.h"
  #include "parse-options.h"
  #include "object-store.h"
 +#include "commit-reach.h"
  
  static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
  static struct string_list changed_submodule_names = STRING_LIST_INIT_DUP;
@@@ -51,6 -50,24 +51,24 @@@ int is_gitmodules_unmerged(const struc
        return 0;
  }
  
+ /*
+  * Check if the .gitmodules file is safe to write.
+  *
+  * Writing to the .gitmodules file requires that the file exists in the
+  * working tree or, if it doesn't, that a brand new .gitmodules file is going
+  * to be created (i.e. it's neither in the index nor in the current branch).
+  *
+  * It is not safe to write to .gitmodules if it's not in the working tree but
+  * it is in the index or in the current branch, because writing new values
+  * (and staging them) would blindly overwrite ALL the old content.
+  */
+ int is_writing_gitmodules_ok(void)
+ {
+       struct object_id oid;
+       return file_exists(GITMODULES_FILE) ||
+               (get_oid(GITMODULES_INDEX, &oid) < 0 && get_oid(GITMODULES_HEAD, &oid) < 0);
+ }
  /*
   * Check if the .gitmodules file has unstaged modifications.  This must be
   * checked before allowing modifications to the .gitmodules file with the
@@@ -66,7 -83,8 +84,7 @@@ int is_staging_gitmodules_ok(struct ind
        if ((pos >= 0) && (pos < istate->cache_nr)) {
                struct stat st;
                if (lstat(GITMODULES_FILE, &st) == 0 &&
 -                  ie_match_stat(istate, istate->cache[pos], &st,
 -                                CE_MATCH_IGNORE_FSMONITOR) & DATA_CHANGED)
 +                  ie_match_stat(istate, istate->cache[pos], &st, 0) & DATA_CHANGED)
                        return 0;
        }
  
@@@ -89,6 -107,7 +107,7 @@@ int update_path_in_gitmodules(const cha
  {
        struct strbuf entry = STRBUF_INIT;
        const struct submodule *submodule;
+       int ret;
  
        if (!file_exists(GITMODULES_FILE)) /* Do nothing without .gitmodules */
                return -1;
        strbuf_addstr(&entry, "submodule.");
        strbuf_addstr(&entry, submodule->name);
        strbuf_addstr(&entry, ".path");
-       if (git_config_set_in_file_gently(GITMODULES_FILE, entry.buf, newpath) < 0) {
-               /* Maybe the user already did that, don't error out here */
-               warning(_("Could not update .gitmodules entry %s"), entry.buf);
-               strbuf_release(&entry);
-               return -1;
-       }
+       ret = config_set_in_gitmodules_file_gently(entry.buf, newpath);
        strbuf_release(&entry);
-       return 0;
+       return ret;
  }
  
  /*
@@@ -428,7 -442,7 +442,7 @@@ static int prepare_submodule_summary(st
  {
        struct commit_list *list;
  
 -      init_revisions(rev, NULL);
 +      repo_init_revisions(the_repository, rev, NULL);
        setup_revisions(0, NULL, rev, NULL);
        rev->left_right = 1;
        rev->first_parent_only = 1;
@@@ -536,7 -550,7 +550,7 @@@ static void show_submodule_header(struc
                        fast_backward = 1;
        }
  
 -      if (!oidcmp(one, two)) {
 +      if (oideq(one, two)) {
                strbuf_release(&sb);
                return;
        }
@@@ -694,7 -708,6 +708,7 @@@ static struct oid_array *submodule_comm
  }
  
  struct collect_changed_submodules_cb_data {
 +      struct repository *repo;
        struct string_list *changed;
        const struct object_id *commit_oid;
  };
@@@ -734,7 -747,7 +748,7 @@@ static void collect_changed_submodules_
                if (!S_ISGITLINK(p->two->mode))
                        continue;
  
 -              submodule = submodule_from_path(the_repository,
 +              submodule = submodule_from_path(me->repo,
                                                commit_oid, p->two->path);
                if (submodule)
                        name = submodule->name;
                        name = default_name_or_path(p->two->path);
                        /* make sure name does not collide with existing one */
                        if (name)
 -                              submodule = submodule_from_name(the_repository,
 +                              submodule = submodule_from_name(me->repo,
                                                                commit_oid, name);
                        if (submodule) {
                                warning("Submodule in commit %s at path: "
   * have a corresponding 'struct oid_array' (in the 'util' field) which lists
   * what the submodule pointers were updated to during the change.
   */
 -static void collect_changed_submodules(struct string_list *changed,
 +static void collect_changed_submodules(struct repository *r,
 +                                     struct string_list *changed,
                                       struct argv_array *argv)
  {
        struct rev_info rev;
        const struct commit *commit;
  
 -      init_revisions(&rev, NULL);
 +      repo_init_revisions(r, &rev, NULL);
        setup_revisions(argv->argc, argv->argv, &rev, NULL);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
        while ((commit = get_revision(&rev))) {
                struct rev_info diff_rev;
                struct collect_changed_submodules_cb_data data;
 +              data.repo = r;
                data.changed = changed;
                data.commit_oid = &commit->object.oid;
  
 -              init_revisions(&diff_rev, NULL);
 +              repo_init_revisions(r, &diff_rev, NULL);
                diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
                diff_rev.diffopt.format_callback = collect_changed_submodules_cb;
                diff_rev.diffopt.format_callback_data = &data;
@@@ -818,7 -829,6 +832,7 @@@ static int append_oid_to_argv(const str
  }
  
  struct has_commit_data {
 +      struct repository *repo;
        int result;
        const char *path;
  };
@@@ -827,7 -837,7 +841,7 @@@ static int check_has_commit(const struc
  {
        struct has_commit_data *cb = data;
  
 -      enum object_type type = oid_object_info(the_repository, oid, NULL);
 +      enum object_type type = oid_object_info(cb->repo, oid, NULL);
  
        switch (type) {
        case OBJ_COMMIT:
        }
  }
  
 -static int submodule_has_commits(const char *path, struct oid_array *commits)
 +static int submodule_has_commits(struct repository *r,
 +                               const char *path,
 +                               struct oid_array *commits)
  {
 -      struct has_commit_data has_commit = { 1, path };
 +      struct has_commit_data has_commit = { r, 1, path };
  
        /*
         * Perform a cheap, but incorrect check for the existence of 'commits'.
        return has_commit.result;
  }
  
 -static int submodule_needs_pushing(const char *path, struct oid_array *commits)
 +static int submodule_needs_pushing(struct repository *r,
 +                                 const char *path,
 +                                 struct oid_array *commits)
  {
 -      if (!submodule_has_commits(path, commits))
 +      if (!submodule_has_commits(r, path, commits))
                /*
                 * NOTE: We do consider it safe to return "no" here. The
                 * correct answer would be "We do not know" instead of
        return 0;
  }
  
 -int find_unpushed_submodules(struct oid_array *commits,
 -              const char *remotes_name, struct string_list *needs_pushing)
 +int find_unpushed_submodules(struct repository *r,
 +                           struct oid_array *commits,
 +                           const char *remotes_name,
 +                           struct string_list *needs_pushing)
  {
        struct string_list submodules = STRING_LIST_INIT_DUP;
        struct string_list_item *name;
        argv_array_push(&argv, "--not");
        argv_array_pushf(&argv, "--remotes=%s", remotes_name);
  
 -      collect_changed_submodules(&submodules, &argv);
 +      collect_changed_submodules(r, &submodules, &argv);
  
        for_each_string_list_item(name, &submodules) {
                struct oid_array *commits = name->util;
                const struct submodule *submodule;
                const char *path = NULL;
  
 -              submodule = submodule_from_name(the_repository, &null_oid, name->string);
 +              submodule = submodule_from_name(r, &null_oid, name->string);
                if (submodule)
                        path = submodule->path;
                else
                if (!path)
                        continue;
  
 -              if (submodule_needs_pushing(path, commits))
 +              if (submodule_needs_pushing(r, path, commits))
                        string_list_insert(needs_pushing, path);
        }
  
@@@ -1054,8 -1058,7 +1068,8 @@@ static void submodule_push_check(const 
                die("process for submodule '%s' failed", path);
  }
  
 -int push_unpushed_submodules(struct oid_array *commits,
 +int push_unpushed_submodules(struct repository *r,
 +                           struct oid_array *commits,
                             const struct remote *remote,
                             const struct refspec *rs,
                             const struct string_list *push_options,
        int i, ret = 1;
        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
  
 -      if (!find_unpushed_submodules(commits, remote->name, &needs_pushing))
 +      if (!find_unpushed_submodules(r, commits,
 +                                    remote->name, &needs_pushing))
                return 1;
  
        /*
@@@ -1122,14 -1124,14 +1136,14 @@@ void check_for_new_submodule_commits(st
        oid_array_append(&ref_tips_after_fetch, oid);
  }
  
 -static void calculate_changed_submodule_paths(void)
 +static void calculate_changed_submodule_paths(struct repository *r)
  {
        struct argv_array argv = ARGV_ARRAY_INIT;
        struct string_list changed_submodules = STRING_LIST_INIT_DUP;
        const struct string_list_item *name;
  
        /* No need to check if there are no submodules configured */
 -      if (!submodule_from_path(the_repository, NULL, NULL))
 +      if (!submodule_from_path(r, NULL, NULL))
                return;
  
        argv_array_push(&argv, "--"); /* argv[0] program name */
         * Collect all submodules (whether checked out or not) for which new
         * commits have been recorded upstream in "changed_submodule_names".
         */
 -      collect_changed_submodules(&changed_submodules, &argv);
 +      collect_changed_submodules(r, &changed_submodules, &argv);
  
        for_each_string_list_item(name, &changed_submodules) {
                struct oid_array *commits = name->util;
                const struct submodule *submodule;
                const char *path = NULL;
  
 -              submodule = submodule_from_name(the_repository, &null_oid, name->string);
 +              submodule = submodule_from_name(r, &null_oid, name->string);
                if (submodule)
                        path = submodule->path;
                else
                if (!path)
                        continue;
  
 -              if (!submodule_has_commits(path, commits))
 +              if (!submodule_has_commits(r, path, commits))
                        string_list_append(&changed_submodule_names, name->string);
        }
  
        initialized_fetch_ref_tips = 0;
  }
  
 -int submodule_touches_in_range(struct object_id *excl_oid,
 +int submodule_touches_in_range(struct repository *r,
 +                             struct object_id *excl_oid,
                               struct object_id *incl_oid)
  {
        struct string_list subs = STRING_LIST_INIT_DUP;
        int ret;
  
        /* No need to check if there are no submodules configured */
 -      if (!submodule_from_path(the_repository, NULL, NULL))
 +      if (!submodule_from_path(r, NULL, NULL))
                return 0;
  
        argv_array_push(&args, "--"); /* args[0] program name */
                argv_array_push(&args, oid_to_hex(excl_oid));
        }
  
 -      collect_changed_submodules(&subs, &args);
 +      collect_changed_submodules(r, &subs, &args);
        ret = subs.nr;
  
        argv_array_clear(&args);
@@@ -1359,7 -1360,7 +1373,7 @@@ int fetch_populated_submodules(struct r
        argv_array_push(&spf.args, "--recurse-submodules-default");
        /* default value, "--submodule-prefix" and its value are added later */
  
 -      calculate_changed_submodule_paths();
 +      calculate_changed_submodule_paths(r);
        run_processes_parallel(max_parallel_jobs,
                               get_next_submodule,
                               fetch_start_failure,
@@@ -1893,7 -1894,7 +1907,7 @@@ const char *get_superproject_working_tr
                 * We're only interested in the name after the tab.
                 */
                super_sub = strchr(sb.buf, '\t') + 1;
 -              super_sub_len = sb.buf + sb.len - super_sub - 1;
 +              super_sub_len = strlen(super_sub);
  
                if (super_sub_len > cwd_len ||
                    strcmp(&cwd[cwd_len - super_sub_len], super_sub))
diff --combined submodule.h
index a8a4fe8d2565a543f6a817eb31d76d5b6804ae40,7a22f71cb952f70733be3da6ab3b240122fe8561..a680214c01a5fda90a21af547389f6333388683c
@@@ -40,6 -40,7 +40,7 @@@ struct submodule_update_strategy 
  #define SUBMODULE_UPDATE_STRATEGY_INIT {SM_UPDATE_UNSPECIFIED, NULL}
  
  int is_gitmodules_unmerged(const struct index_state *istate);
+ int is_writing_gitmodules_ok(void);
  int is_staging_gitmodules_ok(struct index_state *istate);
  int update_path_in_gitmodules(const char *oldpath, const char *newpath);
  int remove_path_from_gitmodules(const char *path);
@@@ -102,16 -103,13 +103,16 @@@ int add_submodule_odb(const char *path)
   * Checks if there are submodule changes in a..b. If a is the null OID,
   * checks b and all its ancestors instead.
   */
 -int submodule_touches_in_range(struct object_id *a,
 +int submodule_touches_in_range(struct repository *r,
 +                             struct object_id *a,
                               struct object_id *b);
 -int find_unpushed_submodules(struct oid_array *commits,
 +int find_unpushed_submodules(struct repository *r,
 +                           struct oid_array *commits,
                             const char *remotes_name,
                             struct string_list *needs_pushing);
  struct refspec;
 -int push_unpushed_submodules(struct oid_array *commits,
 +int push_unpushed_submodules(struct repository *r,
 +                           struct oid_array *commits,
                             const struct remote *remote,
                             const struct refspec *rs,
                             const struct string_list *push_options,
diff --combined t/helper/test-tool.c
index 5df8b682aa8ab0ce305dd8f22d3b7c4780331d71,a25fa80ca2e4422330ba76d9e95650b928fa65c1..bfb195b1a828a34988912e66f9e07f493588d5b4
@@@ -14,9 -14,7 +14,9 @@@ static struct test_cmd cmds[] = 
        { "delta", cmd__delta },
        { "drop-caches", cmd__drop_caches },
        { "dump-cache-tree", cmd__dump_cache_tree },
 +      { "dump-fsmonitor", cmd__dump_fsmonitor },
        { "dump-split-index", cmd__dump_split_index },
 +      { "dump-untracked-cache", cmd__dump_untracked_cache },
        { "example-decorate", cmd__example_decorate },
        { "genrandom", cmd__genrandom },
        { "hashmap", cmd__hashmap },
        { "mergesort", cmd__mergesort },
        { "mktemp", cmd__mktemp },
        { "online-cpus", cmd__online_cpus },
 +      { "parse-options", cmd__parse_options },
        { "path-utils", cmd__path_utils },
 +      { "pkt-line", cmd__pkt_line },
        { "prio-queue", cmd__prio_queue },
 +      { "reach", cmd__reach },
        { "read-cache", cmd__read_cache },
 +      { "read-midx", cmd__read_midx },
        { "ref-store", cmd__ref_store },
        { "regex", cmd__regex },
        { "repository", cmd__repository },
        { "revision-walking", cmd__revision_walking },
        { "run-command", cmd__run_command },
        { "scrap-cache-tree", cmd__scrap_cache_tree },
 -      { "sha1-array", cmd__sha1_array },
        { "sha1", cmd__sha1 },
 +      { "sha1-array", cmd__sha1_array },
        { "sigchain", cmd__sigchain },
        { "strcmp-offset", cmd__strcmp_offset },
        { "string-list", cmd__string_list },
        { "submodule-config", cmd__submodule_config },
+       { "submodule-nested-repo-config", cmd__submodule_nested_repo_config },
        { "subprocess", cmd__subprocess },
        { "urlmatch-normalization", cmd__urlmatch_normalization },
        { "wildmatch", cmd__wildmatch },
 +#ifdef GIT_WINDOWS_NATIVE
 +      { "windows-named-pipe", cmd__windows_named_pipe },
 +#endif
        { "write-cache", cmd__write_cache },
  };
  
 +static NORETURN void die_usage(void)
 +{
 +      size_t i;
 +
 +      fprintf(stderr, "usage: test-tool <toolname> [args]\n");
 +      for (i = 0; i < ARRAY_SIZE(cmds); i++)
 +              fprintf(stderr, "  %s\n", cmds[i].name);
 +      exit(128);
 +}
 +
  int cmd_main(int argc, const char **argv)
  {
        int i;
  
        BUG_exit_code = 99;
        if (argc < 2)
 -              die("I need a test name!");
 +              die_usage();
  
        for (i = 0; i < ARRAY_SIZE(cmds); i++) {
                if (!strcmp(cmds[i].name, argv[1])) {
@@@ -80,6 -62,5 +81,6 @@@
                        return cmds[i].fn(argc, argv);
                }
        }
 -      die("There is no test named '%s'", argv[1]);
 +      error("there is no tool named '%s'", argv[1]);
 +      die_usage();
  }
diff --combined t/helper/test-tool.h
index 71f470b87141a23f881a66349c3c409207a3c7f9,9462e38fb58ee478630d184e576005f61459026b..042f12464b2a17afeb37e01d90bf49ba33ce879a
@@@ -1,5 -1,5 +1,5 @@@
 -#ifndef __TEST_TOOL_H__
 -#define __TEST_TOOL_H__
 +#ifndef TEST_TOOL_H
 +#define TEST_TOOL_H
  
  #include "git-compat-util.h"
  
@@@ -10,9 -10,7 +10,9 @@@ int cmd__date(int argc, const char **ar
  int cmd__delta(int argc, const char **argv);
  int cmd__drop_caches(int argc, const char **argv);
  int cmd__dump_cache_tree(int argc, const char **argv);
 +int cmd__dump_fsmonitor(int argc, const char **argv);
  int cmd__dump_split_index(int argc, const char **argv);
 +int cmd__dump_untracked_cache(int argc, const char **argv);
  int cmd__example_decorate(int argc, const char **argv);
  int cmd__genrandom(int argc, const char **argv);
  int cmd__hashmap(int argc, const char **argv);
@@@ -23,31 -21,25 +23,32 @@@ int cmd__match_trees(int argc, const ch
  int cmd__mergesort(int argc, const char **argv);
  int cmd__mktemp(int argc, const char **argv);
  int cmd__online_cpus(int argc, const char **argv);
 +int cmd__parse_options(int argc, const char **argv);
  int cmd__path_utils(int argc, const char **argv);
 +int cmd__pkt_line(int argc, const char **argv);
  int cmd__prio_queue(int argc, const char **argv);
 +int cmd__reach(int argc, const char **argv);
  int cmd__read_cache(int argc, const char **argv);
 +int cmd__read_midx(int argc, const char **argv);
  int cmd__ref_store(int argc, const char **argv);
  int cmd__regex(int argc, const char **argv);
  int cmd__repository(int argc, const char **argv);
  int cmd__revision_walking(int argc, const char **argv);
  int cmd__run_command(int argc, const char **argv);
  int cmd__scrap_cache_tree(int argc, const char **argv);
 -int cmd__sha1_array(int argc, const char **argv);
  int cmd__sha1(int argc, const char **argv);
 +int cmd__sha1_array(int argc, const char **argv);
  int cmd__sigchain(int argc, const char **argv);
  int cmd__strcmp_offset(int argc, const char **argv);
  int cmd__string_list(int argc, const char **argv);
  int cmd__submodule_config(int argc, const char **argv);
+ int cmd__submodule_nested_repo_config(int argc, const char **argv);
  int cmd__subprocess(int argc, const char **argv);
  int cmd__urlmatch_normalization(int argc, const char **argv);
  int cmd__wildmatch(int argc, const char **argv);
 +#ifdef GIT_WINDOWS_NATIVE
 +int cmd__windows_named_pipe(int argc, const char **argv);
 +#endif
  int cmd__write_cache(int argc, const char **argv);
  
  #endif