Merge branch 'sb/submodule-move-nested'
authorJunio C Hamano <gitster@pobox.com>
Tue, 8 May 2018 06:59:17 +0000 (15:59 +0900)
committerJunio C Hamano <gitster@pobox.com>
Tue, 8 May 2018 06:59:17 +0000 (15:59 +0900)
Moving a submodule that itself has submodule in it with "git mv"
forgot to make necessary adjustment to the nested sub-submodules;
now the codepath learned to recurse into the submodules.

* sb/submodule-move-nested:
submodule: fixup nested submodules after moving the submodule
submodule-config: remove submodule_from_cache
submodule-config: add repository argument to submodule_from_{name, path}
submodule-config: allow submodule_free to handle arbitrary repositories
grep: remove "repo" arg from non-supporting funcs
submodule.h: drop declaration of connect_work_tree_and_git_dir

12 files changed:
1  2 
Documentation/technical/api-submodule-config.txt
builtin/grep.c
builtin/mv.c
builtin/submodule--helper.c
dir.c
dir.h
submodule-config.c
submodule.c
submodule.h
t/helper/test-submodule-config.c
t/t7001-mv.sh
unpack-trees.c
index ee907c4a82a9127c0abc67b8a5dd215a3b0535d4,44a85bbb8bece89ce4aa8f15199af352b2084ed7..fb060893931f2e74c5857c2d03b019e1fa138976
@@@ -4,7 -4,7 +4,7 @@@ submodule config cache AP
  The submodule config cache API allows to read submodule
  configurations/information from specified revisions. Internally
  information is lazily read into a cache that is used to avoid
 -unnecessary parsing of the same .gitmodule files. Lookups can be done by
 +unnecessary parsing of the same .gitmodules files. Lookups can be done by
  submodule path or name.
  
  Usage
@@@ -38,7 -38,7 +38,7 @@@ Data Structure
  Functions
  ---------
  
- `void submodule_free()`::
+ `void submodule_free(struct repository *r)`::
  
        Use these to free the internally cached values.
  
diff --combined builtin/grep.c
index 5f32d2ce84f27bca725c12aa51d2d91a0c38be8b,c1f22fb9fbaaed5bfc441641cd353ee16d887889..6e7bc76785ace33f80251edfcc4feac8ad19d7c5
@@@ -93,7 -93,8 +93,7 @@@ static pthread_cond_t cond_result
  
  static int skip_first_line;
  
 -static void add_work(struct grep_opt *opt, enum grep_source_type type,
 -                   const char *name, const char *path, const void *id)
 +static void add_work(struct grep_opt *opt, const struct grep_source *gs)
  {
        grep_lock();
  
                pthread_cond_wait(&cond_write, &grep_mutex);
        }
  
 -      grep_source_init(&todo[todo_end].source, type, name, path, id);
 +      todo[todo_end].source = *gs;
        if (opt->binary != GREP_BINARY_TEXT)
                grep_source_load_driver(&todo[todo_end].source);
        todo[todo_end].done = 0;
@@@ -307,7 -308,7 +307,7 @@@ static void *lock_and_read_oid_file(con
        void *data;
  
        grep_read_lock();
 -      data = read_sha1_file(oid->hash, type, size);
 +      data = read_object_file(oid, type, size);
        grep_read_unlock();
        return data;
  }
@@@ -317,7 -318,6 +317,7 @@@ static int grep_oid(struct grep_opt *op
                     const char *path)
  {
        struct strbuf pathbuf = STRBUF_INIT;
 +      struct grep_source gs;
  
        if (opt->relative && opt->prefix_length) {
                quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
                strbuf_addstr(&pathbuf, filename);
        }
  
 +      grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
 +      strbuf_release(&pathbuf);
 +
  #ifndef NO_PTHREADS
        if (num_threads) {
 -              add_work(opt, GREP_SOURCE_OID, pathbuf.buf, path, oid);
 -              strbuf_release(&pathbuf);
 +              /*
 +               * add_work() copies gs and thus assumes ownership of
 +               * its fields, so do not call grep_source_clear()
 +               */
 +              add_work(opt, &gs);
                return 0;
        } else
  #endif
        {
 -              struct grep_source gs;
                int hit;
  
 -              grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
 -              strbuf_release(&pathbuf);
                hit = grep_source(opt, &gs);
  
                grep_source_clear(&gs);
  static int grep_file(struct grep_opt *opt, const char *filename)
  {
        struct strbuf buf = STRBUF_INIT;
 +      struct grep_source gs;
  
        if (opt->relative && opt->prefix_length)
                quote_path_relative(filename, opt->prefix, &buf);
        else
                strbuf_addstr(&buf, filename);
  
 +      grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
 +      strbuf_release(&buf);
 +
  #ifndef NO_PTHREADS
        if (num_threads) {
 -              add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename);
 -              strbuf_release(&buf);
 +              /*
 +               * add_work() copies gs and thus assumes ownership of
 +               * its fields, so do not call grep_source_clear()
 +               */
 +              add_work(opt, &gs);
                return 0;
        } else
  #endif
        {
 -              struct grep_source gs;
                int hit;
  
 -              grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
 -              strbuf_release(&buf);
                hit = grep_source(opt, &gs);
  
                grep_source_clear(&gs);
@@@ -453,7 -446,7 +453,7 @@@ static int grep_submodule(struct grep_o
                object = parse_object_or_die(oid, oid_to_hex(oid));
  
                grep_read_lock();
 -              data = read_object_with_reference(object->oid.hash, tree_type,
 +              data = read_object_with_reference(&object->oid, tree_type,
                                                  &size, NULL);
                grep_read_unlock();
  
@@@ -602,8 -595,7 +602,7 @@@ static int grep_tree(struct grep_opt *o
  }
  
  static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
-                      struct object *obj, const char *name, const char *path,
-                      struct repository *repo)
+                      struct object *obj, const char *name, const char *path)
  {
        if (obj->type == OBJ_BLOB)
                return grep_oid(opt, &obj->oid, name, 0, path);
                int hit, len;
  
                grep_read_lock();
 -              data = read_object_with_reference(obj->oid.hash, tree_type,
 +              data = read_object_with_reference(&obj->oid, tree_type,
                                                  &size, NULL);
                grep_read_unlock();
  
                }
                init_tree_desc(&tree, data, size);
                hit = grep_tree(opt, pathspec, &tree, &base, base.len,
-                               obj->type == OBJ_COMMIT, repo);
+                               obj->type == OBJ_COMMIT, the_repository);
                strbuf_release(&base);
                free(data);
                return hit;
        }
 -      die(_("unable to grep from object of type %s"), typename(obj->type));
 +      die(_("unable to grep from object of type %s"), type_name(obj->type));
  }
  
  static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
-                       struct repository *repo,
                        const struct object_array *list)
  {
        unsigned int i;
  
                /* load the gitmodules file for this rev */
                if (recurse_submodules) {
-                       submodule_free();
+                       submodule_free(the_repository);
                        gitmodules_config_oid(&real_obj->oid);
                }
-               if (grep_object(opt, pathspec, real_obj, list->objects[i].name, list->objects[i].path,
-                               repo)) {
+               if (grep_object(opt, pathspec, real_obj, list->objects[i].name,
+                               list->objects[i].path)) {
                        hit = 1;
                        if (opt->status_only)
                                break;
@@@ -840,9 -831,8 +838,9 @@@ int cmd_grep(int argc, const char **arg
                OPT_BOOL('L', "files-without-match",
                        &opt.unmatch_name_only,
                        N_("show only the names of files without match")),
 -              OPT_BOOL('z', "null", &opt.null_following_name,
 -                      N_("print NUL after filenames")),
 +              OPT_BOOL_F('z', "null", &opt.null_following_name,
 +                         N_("print NUL after filenames"),
 +                         PARSE_OPT_NOCOMPLETE),
                OPT_BOOL('c', "count", &opt.count,
                        N_("show the number of matches instead of matching lines")),
                OPT__COLOR(&opt.color, N_("highlight matches")),
                OPT_GROUP(""),
                { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
                        N_("pager"), N_("show matching files in the pager"),
 -                      PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
 -              OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored,
 -                       N_("allow calling of grep(1) (ignored by this build)")),
 +                      PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE,
 +                      NULL, (intptr_t)default_pager },
 +              OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored,
 +                         N_("allow calling of grep(1) (ignored by this build)"),
 +                         PARSE_OPT_NOCOMPLETE),
                OPT_END()
        };
  
                if (cached)
                        die(_("both --cached and trees are given."));
  
-               hit = grep_objects(&opt, &pathspec, the_repository, &list);
+               hit = grep_objects(&opt, &pathspec, &list);
        }
  
        if (num_threads)
diff --combined builtin/mv.c
index 6d141f7a532c08e52f1f5f82330d046c60073f93,b0c5178e0d24a9a87e868c03ec9154e32a00b99d..7a63667d64810c1164cf3acad3cfcc6cedf4010d
@@@ -122,8 -122,7 +122,8 @@@ int cmd_mv(int argc, const char **argv
        struct option builtin_mv_options[] = {
                OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT__DRY_RUN(&show_only, N_("dry run")),
 -              OPT__FORCE(&force, N_("force move/rename even if target exists")),
 +              OPT__FORCE(&force, N_("force move/rename even if target exists"),
 +                         PARSE_OPT_NOCOMPLETE),
                OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")),
                OPT_END(),
        };
                        die_errno(_("renaming '%s' failed"), src);
                }
                if (submodule_gitfile[i]) {
-                       if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
-                               connect_work_tree_and_git_dir(dst, submodule_gitfile[i]);
                        if (!update_path_in_gitmodules(src, dst))
                                gitmodules_modified = 1;
+                       if (submodule_gitfile[i] != SUBMODULE_WITH_GITDIR)
+                               connect_work_tree_and_git_dir(dst,
+                                                             submodule_gitfile[i],
+                                                             1);
                }
  
                if (mode == WORKING_DIRECTORY)
  
                pos = cache_name_pos(src, strlen(src));
                assert(pos >= 0);
 -              if (!show_only)
 -                      rename_cache_entry_at(pos, dst);
 +              rename_cache_entry_at(pos, dst);
        }
  
        if (gitmodules_modified)
                stage_updated_gitmodules(&the_index);
  
 -      if (active_cache_changed &&
 -          write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
 +      if (write_locked_index(&the_index, &lock_file,
 +                             COMMIT_LOCK | SKIP_IF_UNCHANGED))
                die(_("Unable to write new index file"));
  
        return 0;
index a404df3ea494d4e4753e5ca95be06b7eed14f617,ffdc51f426e3781151c03fcd898cfeb43862e8ea..7586f776fe3cf2140c86f1f1842389f186929201
@@@ -455,7 -455,7 +455,7 @@@ static void init_submodule(const char *
  
        displaypath = get_submodule_displaypath(path, prefix);
  
-       sub = submodule_from_path(&null_oid, path);
+       sub = submodule_from_path(the_repository, &null_oid, path);
  
        if (!sub)
                die(_("No url found for submodule path '%s' in .gitmodules"),
@@@ -622,7 -622,7 +622,7 @@@ static void status_submodule(const cha
        struct rev_info rev;
        int diff_files_result;
  
-       if (!submodule_from_path(&null_oid, path))
+       if (!submodule_from_path(the_repository, &null_oid, path))
                die(_("no submodule mapping found in .gitmodules for path '%s'"),
                      path);
  
                             displaypath);
        } else if (!(flags & OPT_CACHED)) {
                struct object_id oid;
 +              struct ref_store *refs = get_submodule_ref_store(path);
  
 -              if (refs_head_ref(get_submodule_ref_store(path),
 -                                handle_submodule_head_ref, &oid))
 +              if (!refs) {
 +                      print_status(flags, '-', path, ce_oid, displaypath);
 +                      goto cleanup;
 +              }
 +              if (refs_head_ref(refs, handle_submodule_head_ref, &oid))
                        die(_("could not resolve HEAD ref inside the "
                              "submodule '%s'"), path);
  
@@@ -746,7 -742,7 +746,7 @@@ static int module_name(int argc, const 
        if (argc != 2)
                usage(_("git submodule--helper name <path>"));
  
-       sub = submodule_from_path(&null_oid, argv[1]);
+       sub = submodule_from_path(the_repository, &null_oid, argv[1]);
  
        if (!sub)
                die(_("no submodule mapping found in .gitmodules for path '%s'"),
@@@ -777,7 -773,7 +777,7 @@@ static void sync_submodule(const char *
        if (!is_submodule_active(the_repository, path))
                return;
  
-       sub = submodule_from_path(&null_oid, path);
+       sub = submodule_from_path(the_repository, &null_oid, path);
  
        if (sub && sub->url) {
                if (starts_with_dot_dot_slash(sub->url) ||
@@@ -930,7 -926,7 +930,7 @@@ static void deinit_submodule(const cha
        struct strbuf sb_config = STRBUF_INIT;
        char *sub_git_dir = xstrfmt("%s/.git", path);
  
-       sub = submodule_from_path(&null_oid, path);
+       sub = submodule_from_path(the_repository, &null_oid, path);
  
        if (!sub || !sub->name)
                goto cleanup;
@@@ -1024,7 -1020,7 +1024,7 @@@ static int module_deinit(int argc, cons
  
        struct option module_deinit_options[] = {
                OPT__QUIET(&quiet, N_("Suppress submodule status output")),
 -              OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes")),
 +              OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes"), 0),
                OPT_BOOL(0, "all", &all, N_("Unregister all submodules")),
                OPT_END()
        };
                die(_("Use '--all' if you really want to deinitialize all submodules"));
  
        if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
 -              BUG("module_list_compute should not choke on empty pathspec");
 +              return 1;
  
        info.prefix = prefix;
        if (quiet)
@@@ -1264,8 -1260,7 +1264,7 @@@ static int module_clone(int argc, cons
                strbuf_reset(&sb);
        }
  
-       /* Connect module worktree and git dir */
-       connect_work_tree_and_git_dir(path, sm_gitdir);
+       connect_work_tree_and_git_dir(path, sm_gitdir, 0);
  
        p = git_pathdup_submodule(path, "config");
        if (!p)
@@@ -1372,7 -1367,7 +1371,7 @@@ static int prepare_to_clone_next_submod
                goto cleanup;
        }
  
-       sub = submodule_from_path(&null_oid, ce->name);
+       sub = submodule_from_path(the_repository, &null_oid, ce->name);
  
        if (suc->recursive_prefix)
                displaypath = relative_path(suc->recursive_prefix,
@@@ -1655,7 -1650,7 +1654,7 @@@ static const char *remote_submodule_bra
        const char *branch = NULL;
        char *key;
  
-       sub = submodule_from_path(&null_oid, path);
+       sub = submodule_from_path(the_repository, &null_oid, path);
        if (!sub)
                return NULL;
  
diff --combined dir.c
index 63a917be45db99c278cc86012ff74718043dc63d,4f401b6a3c8d54e9619b6bfe537cdd1ec2a8c7e1..be08d3d296f6d565202fc51586f928b5e274e8a9
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -19,6 -19,7 +19,7 @@@
  #include "varint.h"
  #include "ewah/ewok.h"
  #include "fsmonitor.h"
+ #include "submodule-config.h"
  
  /*
   * Tells read_directory_recursive how a file or directory should be treated.
@@@ -231,10 -232,12 +232,10 @@@ int within_depth(const char *name, int 
   *     1 along with { data, size } of the (possibly augmented) buffer
   *       when successful.
   *
 - * Optionally updates the given sha1_stat with the given OID (when valid).
 + * Optionally updates the given oid_stat with the given OID (when valid).
   */
 -static int do_read_blob(const struct object_id *oid,
 -                      struct sha1_stat *sha1_stat,
 -                      size_t *size_out,
 -                      char **data_out)
 +static int do_read_blob(const struct object_id *oid, struct oid_stat *oid_stat,
 +                      size_t *size_out, char **data_out)
  {
        enum object_type type;
        unsigned long sz;
        *size_out = 0;
        *data_out = NULL;
  
 -      data = read_sha1_file(oid->hash, &type, &sz);
 +      data = read_object_file(oid, &type, &sz);
        if (!data || type != OBJ_BLOB) {
                free(data);
                return -1;
        }
  
 -      if (sha1_stat) {
 -              memset(&sha1_stat->stat, 0, sizeof(sha1_stat->stat));
 -              hashcpy(sha1_stat->sha1, oid->hash);
 +      if (oid_stat) {
 +              memset(&oid_stat->stat, 0, sizeof(oid_stat->stat));
 +              oidcpy(&oid_stat->oid, oid);
        }
  
        if (sz == 0) {
@@@ -652,8 -655,9 +653,8 @@@ void add_exclude(const char *string, co
  
  static int read_skip_worktree_file_from_index(const struct index_state *istate,
                                              const char *path,
 -                                            size_t *size_out,
 -                                            char **data_out,
 -                                            struct sha1_stat *sha1_stat)
 +                                            size_t *size_out, char **data_out,
 +                                            struct oid_stat *oid_stat)
  {
        int pos, len;
  
        if (!ce_skip_worktree(istate->cache[pos]))
                return -1;
  
 -      return do_read_blob(&istate->cache[pos]->oid, sha1_stat, size_out, data_out);
 +      return do_read_blob(&istate->cache[pos]->oid, oid_stat, size_out, data_out);
  }
  
  /*
@@@ -771,16 -775,7 +772,16 @@@ static void invalidate_directory(struc
                                 struct untracked_cache_dir *dir)
  {
        int i;
 -      uc->dir_invalidated++;
 +
 +      /*
 +       * Invalidation increment here is just roughly correct. If
 +       * untracked_nr or any of dirs[].recurse is non-zero, we
 +       * should increment dir_invalidated too. But that's more
 +       * expensive to do.
 +       */
 +      if (dir->valid)
 +              uc->dir_invalidated++;
 +
        dir->valid = 0;
        dir->untracked_nr = 0;
        for (i = 0; i < dir->dirs_nr; i++)
@@@ -801,8 -796,9 +802,8 @@@ static int add_excludes_from_buffer(cha
   * ss_valid is non-zero, "ss" must contain good value as input.
   */
  static int add_excludes(const char *fname, const char *base, int baselen,
 -                      struct exclude_list *el,
 -                      struct index_state *istate,
 -                      struct sha1_stat *sha1_stat)
 +                      struct exclude_list *el, struct index_state *istate,
 +                      struct oid_stat *oid_stat)
  {
        struct stat st;
        int r;
                        return -1;
                r = read_skip_worktree_file_from_index(istate, fname,
                                                       &size, &buf,
 -                                                     sha1_stat);
 +                                                     oid_stat);
                if (r != 1)
                        return r;
        } else {
                size = xsize_t(st.st_size);
                if (size == 0) {
 -                      if (sha1_stat) {
 -                              fill_stat_data(&sha1_stat->stat, &st);
 -                              hashcpy(sha1_stat->sha1, EMPTY_BLOB_SHA1_BIN);
 -                              sha1_stat->valid = 1;
 +                      if (oid_stat) {
 +                              fill_stat_data(&oid_stat->stat, &st);
 +                              oidcpy(&oid_stat->oid, &empty_blob_oid);
 +                              oid_stat->valid = 1;
                        }
                        close(fd);
                        return 0;
                }
                buf[size++] = '\n';
                close(fd);
 -              if (sha1_stat) {
 +              if (oid_stat) {
                        int pos;
 -                      if (sha1_stat->valid &&
 -                          !match_stat_data_racy(istate, &sha1_stat->stat, &st))
 +                      if (oid_stat->valid &&
 +                          !match_stat_data_racy(istate, &oid_stat->stat, &st))
                                ; /* no content change, ss->sha1 still good */
                        else if (istate &&
                                 (pos = index_name_pos(istate, fname, strlen(fname))) >= 0 &&
                                 !ce_stage(istate->cache[pos]) &&
                                 ce_uptodate(istate->cache[pos]) &&
                                 !would_convert_to_git(istate, fname))
 -                              hashcpy(sha1_stat->sha1,
 -                                      istate->cache[pos]->oid.hash);
 +                              oidcpy(&oid_stat->oid,
 +                                     &istate->cache[pos]->oid);
                        else
 -                              hash_sha1_file(buf, size, "blob", sha1_stat->sha1);
 -                      fill_stat_data(&sha1_stat->stat, &st);
 -                      sha1_stat->valid = 1;
 +                              hash_object_file(buf, size, "blob",
 +                                               &oid_stat->oid);
 +                      fill_stat_data(&oid_stat->stat, &st);
 +                      oid_stat->valid = 1;
                }
        }
  
@@@ -936,7 -931,7 +937,7 @@@ struct exclude_list *add_exclude_list(s
   * Used to set up core.excludesfile and .git/info/exclude lists.
   */
  static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
 -                                   struct sha1_stat *sha1_stat)
 +                                   struct oid_stat *oid_stat)
  {
        struct exclude_list *el;
        /*
        if (!dir->untracked)
                dir->unmanaged_exclude_files++;
        el = add_exclude_list(dir, EXC_FILE, fname);
 -      if (add_excludes(fname, "", 0, el, NULL, sha1_stat) < 0)
 +      if (add_excludes(fname, "", 0, el, NULL, oid_stat) < 0)
                die("cannot use %s as an exclude file", fname);
  }
  
@@@ -1186,7 -1181,7 +1187,7 @@@ static void prep_exclude(struct dir_str
  
        while (current < baselen) {
                const char *cp;
 -              struct sha1_stat sha1_stat;
 +              struct oid_stat oid_stat;
  
                stk = xcalloc(1, sizeof(*stk));
                if (current < 0) {
                }
  
                /* Try to read per-directory file */
 -              hashclr(sha1_stat.sha1);
 -              sha1_stat.valid = 0;
 +              oidclr(&oid_stat.oid);
 +              oid_stat.valid = 0;
                if (dir->exclude_per_dir &&
                    /*
                     * If we know that no files have been added in
                        strbuf_addstr(&sb, dir->exclude_per_dir);
                        el->src = strbuf_detach(&sb, NULL);
                        add_excludes(el->src, el->src, stk->baselen, el, istate,
 -                                   untracked ? &sha1_stat : NULL);
 +                                   untracked ? &oid_stat : NULL);
                }
                /*
                 * NEEDSWORK: when untracked cache is enabled, prep_exclude()
                 * order, though, if you do that.
                 */
                if (untracked &&
 -                  hashcmp(sha1_stat.sha1, untracked->exclude_sha1)) {
 +                  hashcmp(oid_stat.oid.hash, untracked->exclude_sha1)) {
                        invalidate_gitignore(dir->untracked, untracked);
 -                      hashcpy(untracked->exclude_sha1, sha1_stat.sha1);
 +                      hashcpy(untracked->exclude_sha1, oid_stat.oid.hash);
                }
                dir->exclude_stack = stk;
                current = stk->baselen;
@@@ -1779,7 -1774,7 +1780,7 @@@ static enum path_treatment treat_path(s
        if (!de)
                return treat_path_fast(dir, untracked, cdir, istate, path,
                                       baselen, pathspec);
 -      if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
 +      if (is_dot_or_dotdot(de->d_name) || !fspathcmp(de->d_name, ".git"))
                return path_none;
        strbuf_setlen(path, baselen);
        strbuf_addstr(path, de->d_name);
@@@ -1815,19 -1810,24 +1816,19 @@@ static int valid_cached_dir(struct dir_
         */
        refresh_fsmonitor(istate);
        if (!(dir->untracked->use_fsmonitor && untracked->valid)) {
 -              if (stat(path->len ? path->buf : ".", &st)) {
 -                      invalidate_directory(dir->untracked, untracked);
 +              if (lstat(path->len ? path->buf : ".", &st)) {
                        memset(&untracked->stat_data, 0, sizeof(untracked->stat_data));
                        return 0;
                }
                if (!untracked->valid ||
                        match_stat_data_racy(istate, &untracked->stat_data, &st)) {
 -                      if (untracked->valid)
 -                              invalidate_directory(dir->untracked, untracked);
                        fill_stat_data(&untracked->stat_data, &st);
                        return 0;
                }
        }
  
 -      if (untracked->check_only != !!check_only) {
 -              invalidate_directory(dir->untracked, untracked);
 +      if (untracked->check_only != !!check_only)
                return 0;
 -      }
  
        /*
         * prep_exclude will be called eventually on this directory,
@@@ -1854,20 -1854,13 +1855,20 @@@ static int open_cached_dir(struct cache
                           struct strbuf *path,
                           int check_only)
  {
 +      const char *c_path;
 +
        memset(cdir, 0, sizeof(*cdir));
        cdir->untracked = untracked;
        if (valid_cached_dir(dir, untracked, istate, path, check_only))
                return 0;
 -      cdir->fdir = opendir(path->len ? path->buf : ".");
 -      if (dir->untracked)
 +      c_path = path->len ? path->buf : ".";
 +      cdir->fdir = opendir(c_path);
 +      if (!cdir->fdir)
 +              warning_errno(_("could not open directory '%s'"), c_path);
 +      if (dir->untracked) {
 +              invalidate_directory(dir->untracked, untracked);
                dir->untracked->dir_opened++;
 +      }
        if (!cdir->fdir)
                return -1;
        return 0;
@@@ -2172,13 -2165,8 +2173,13 @@@ static struct untracked_cache_dir *vali
                                                      const struct pathspec *pathspec)
  {
        struct untracked_cache_dir *root;
 +      static int untracked_cache_disabled = -1;
  
 -      if (!dir->untracked || getenv("GIT_DISABLE_UNTRACKED_CACHE"))
 +      if (!dir->untracked)
 +              return NULL;
 +      if (untracked_cache_disabled < 0)
 +              untracked_cache_disabled = git_env_bool("GIT_DISABLE_UNTRACKED_CACHE", 0);
 +      if (untracked_cache_disabled)
                return NULL;
  
        /*
  
        /* Validate $GIT_DIR/info/exclude and core.excludesfile */
        root = dir->untracked->root;
 -      if (hashcmp(dir->ss_info_exclude.sha1,
 -                  dir->untracked->ss_info_exclude.sha1)) {
 +      if (oidcmp(&dir->ss_info_exclude.oid,
 +                 &dir->untracked->ss_info_exclude.oid)) {
                invalidate_gitignore(dir->untracked, root);
                dir->untracked->ss_info_exclude = dir->ss_info_exclude;
        }
 -      if (hashcmp(dir->ss_excludes_file.sha1,
 -                  dir->untracked->ss_excludes_file.sha1)) {
 +      if (oidcmp(&dir->ss_excludes_file.oid,
 +                 &dir->untracked->ss_excludes_file.oid)) {
                invalidate_gitignore(dir->untracked, root);
                dir->untracked->ss_excludes_file = dir->ss_excludes_file;
        }
@@@ -2261,7 -2249,6 +2262,7 @@@ int read_directory(struct dir_struct *d
                   const char *path, int len, const struct pathspec *pathspec)
  {
        struct untracked_cache_dir *untracked;
 +      uint64_t start = getnanotime();
  
        if (has_symlink_leading_path(path, len))
                return dir->nr;
                dir->nr = i;
        }
  
 +      trace_performance_since(start, "read directory %.*s", len, path);
        if (dir->untracked) {
 +              static int force_untracked_cache = -1;
                static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
 +
 +              if (force_untracked_cache < 0)
 +                      force_untracked_cache =
 +                              git_env_bool("GIT_FORCE_UNTRACKED_CACHE", 0);
                trace_printf_key(&trace_untracked_stats,
                                 "node creation: %u\n"
                                 "gitignore invalidation: %u\n"
                                 dir->untracked->gitignore_invalidated,
                                 dir->untracked->dir_invalidated,
                                 dir->untracked->dir_opened);
 -              if (dir->untracked == istate->untracked &&
 +              if (force_untracked_cache &&
 +                      dir->untracked == istate->untracked &&
                    (dir->untracked->dir_opened ||
                     dir->untracked->gitignore_invalidated ||
                     dir->untracked->dir_invalidated))
@@@ -2659,8 -2639,8 +2660,8 @@@ void write_untracked_extension(struct s
        FLEX_ALLOC_MEM(ouc, exclude_per_dir, untracked->exclude_per_dir, len);
        stat_data_to_disk(&ouc->info_exclude_stat, &untracked->ss_info_exclude.stat);
        stat_data_to_disk(&ouc->excludes_file_stat, &untracked->ss_excludes_file.stat);
 -      hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.sha1);
 -      hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.sha1);
 +      hashcpy(ouc->info_exclude_sha1, untracked->ss_info_exclude.oid.hash);
 +      hashcpy(ouc->excludes_file_sha1, untracked->ss_excludes_file.oid.hash);
        ouc->dir_flags = htonl(untracked->dir_flags);
  
        varint_len = encode_varint(untracked->ident.len, varbuf);
@@@ -2837,12 -2817,13 +2838,12 @@@ static void read_sha1(size_t pos, void 
        rd->data += 20;
  }
  
 -static void load_sha1_stat(struct sha1_stat *sha1_stat,
 -                         const unsigned char *data,
 -                         const unsigned char *sha1)
 +static void load_oid_stat(struct oid_stat *oid_stat, const unsigned char *data,
 +                        const unsigned char *sha1)
  {
 -      stat_data_from_disk(&sha1_stat->stat, data);
 -      hashcpy(sha1_stat->sha1, sha1);
 -      sha1_stat->valid = 1;
 +      stat_data_from_disk(&oid_stat->stat, data);
 +      hashcpy(oid_stat->oid.hash, sha1);
 +      oid_stat->valid = 1;
  }
  
  struct untracked_cache *read_untracked_extension(const void *data, unsigned long sz)
        uc = xcalloc(1, sizeof(*uc));
        strbuf_init(&uc->ident, ident_len);
        strbuf_add(&uc->ident, ident, ident_len);
 -      load_sha1_stat(&uc->ss_info_exclude,
 -                     next + ouc_offset(info_exclude_stat),
 -                     next + ouc_offset(info_exclude_sha1));
 -      load_sha1_stat(&uc->ss_excludes_file,
 -                     next + ouc_offset(excludes_file_stat),
 -                     next + ouc_offset(excludes_file_sha1));
 +      load_oid_stat(&uc->ss_info_exclude,
 +                    next + ouc_offset(info_exclude_stat),
 +                    next + ouc_offset(info_exclude_sha1));
 +      load_oid_stat(&uc->ss_excludes_file,
 +                    next + ouc_offset(excludes_file_stat),
 +                    next + ouc_offset(excludes_file_sha1));
        uc->dir_flags = get_be32(next + ouc_offset(dir_flags));
        exclude_per_dir = (const char *)next + ouc_offset(exclude_per_dir);
        uc->exclude_per_dir = xstrdup(exclude_per_dir);
@@@ -2988,12 -2969,10 +2989,12 @@@ static int invalidate_one_component(str
  }
  
  void untracked_cache_invalidate_path(struct index_state *istate,
 -                                   const char *path)
 +                                   const char *path, int safe_path)
  {
        if (!istate->untracked || !istate->untracked->root)
                return;
 +      if (!safe_path && !verify_path(path))
 +              return;
        invalidate_one_component(istate->untracked, istate->untracked->root,
                                 path, strlen(path));
  }
  void untracked_cache_remove_from_index(struct index_state *istate,
                                       const char *path)
  {
 -      untracked_cache_invalidate_path(istate, path);
 +      untracked_cache_invalidate_path(istate, path, 1);
  }
  
  void untracked_cache_add_to_index(struct index_state *istate,
                                  const char *path)
  {
 -      untracked_cache_invalidate_path(istate, path);
 +      untracked_cache_invalidate_path(istate, path, 1);
  }
  
- /* Update gitfile and core.worktree setting to connect work tree and git dir */
- void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
+ static void connect_wt_gitdir_in_nested(const char *sub_worktree,
+                                       const char *sub_gitdir)
+ {
+       int i;
+       struct repository subrepo;
+       struct strbuf sub_wt = STRBUF_INIT;
+       struct strbuf sub_gd = STRBUF_INIT;
+       const struct submodule *sub;
+       /* If the submodule has no working tree, we can ignore it. */
+       if (repo_init(&subrepo, sub_gitdir, sub_worktree))
+               return;
+       if (repo_read_index(&subrepo) < 0)
+               die("index file corrupt in repo %s", subrepo.gitdir);
+       for (i = 0; i < subrepo.index->cache_nr; i++) {
+               const struct cache_entry *ce = subrepo.index->cache[i];
+               if (!S_ISGITLINK(ce->ce_mode))
+                       continue;
+               while (i + 1 < subrepo.index->cache_nr &&
+                      !strcmp(ce->name, subrepo.index->cache[i + 1]->name))
+                       /*
+                        * Skip entries with the same name in different stages
+                        * to make sure an entry is returned only once.
+                        */
+                       i++;
+               sub = submodule_from_path(&subrepo, &null_oid, ce->name);
+               if (!sub || !is_submodule_active(&subrepo, ce->name))
+                       /* .gitmodules broken or inactive sub */
+                       continue;
+               strbuf_reset(&sub_wt);
+               strbuf_reset(&sub_gd);
+               strbuf_addf(&sub_wt, "%s/%s", sub_worktree, sub->path);
+               strbuf_addf(&sub_gd, "%s/modules/%s", sub_gitdir, sub->name);
+               connect_work_tree_and_git_dir(sub_wt.buf, sub_gd.buf, 1);
+       }
+       strbuf_release(&sub_wt);
+       strbuf_release(&sub_gd);
+       repo_clear(&subrepo);
+ }
+ void connect_work_tree_and_git_dir(const char *work_tree_,
+                                  const char *git_dir_,
+                                  int recurse_into_nested)
  {
        struct strbuf gitfile_sb = STRBUF_INIT;
        struct strbuf cfg_sb = STRBUF_INIT;
        strbuf_release(&gitfile_sb);
        strbuf_release(&cfg_sb);
        strbuf_release(&rel_path);
+       if (recurse_into_nested)
+               connect_wt_gitdir_in_nested(work_tree, git_dir);
        free(work_tree);
        free(git_dir);
  }
@@@ -3054,5 -3086,5 +3108,5 @@@ void relocate_gitdir(const char *path, 
                die_errno(_("could not migrate git directory from '%s' to '%s'"),
                        old_git_dir, new_git_dir);
  
-       connect_work_tree_and_git_dir(path, new_git_dir);
+       connect_work_tree_and_git_dir(path, new_git_dir, 0);
  }
diff --combined dir.h
index b0758b82a20017dd3ce29c54454678f026718078,d2545a7685109688d3ab62143a18e285b3da6119..3870193e527b31186d5965888040af6c810e73ee
--- 1/dir.h
--- 2/dir.h
+++ b/dir.h
@@@ -74,9 -74,9 +74,9 @@@ struct exclude_list_group 
        struct exclude_list *el;
  };
  
 -struct sha1_stat {
 +struct oid_stat {
        struct stat_data stat;
 -      unsigned char sha1[20];
 +      struct object_id oid;
        int valid;
  };
  
@@@ -124,8 -124,8 +124,8 @@@ struct untracked_cache_dir 
  };
  
  struct untracked_cache {
 -      struct sha1_stat ss_info_exclude;
 -      struct sha1_stat ss_excludes_file;
 +      struct oid_stat ss_info_exclude;
 +      struct oid_stat ss_excludes_file;
        const char *exclude_per_dir;
        struct strbuf ident;
        /*
@@@ -195,8 -195,8 +195,8 @@@ struct dir_struct 
  
        /* Enable untracked file cache if set */
        struct untracked_cache *untracked;
 -      struct sha1_stat ss_info_exclude;
 -      struct sha1_stat ss_excludes_file;
 +      struct oid_stat ss_info_exclude;
 +      struct oid_stat ss_excludes_file;
        unsigned unmanaged_exclude_files;
  };
  
@@@ -350,7 -350,7 +350,7 @@@ static inline int dir_path_match(const 
  int cmp_dir_entry(const void *p1, const void *p2);
  int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in);
  
 -void untracked_cache_invalidate_path(struct index_state *, const char *);
 +void untracked_cache_invalidate_path(struct index_state *, const char *, int safe_path);
  void untracked_cache_remove_from_index(struct index_state *, const char *);
  void untracked_cache_add_to_index(struct index_state *, const char *);
  
@@@ -359,7 -359,17 +359,17 @@@ struct untracked_cache *read_untracked_
  void write_untracked_extension(struct strbuf *out, struct untracked_cache *untracked);
  void add_untracked_cache(struct index_state *istate);
  void remove_untracked_cache(struct index_state *istate);
- extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
+ /*
+  * Connect a worktree to a git directory by creating (or overwriting) a
+  * '.git' file containing the location of the git directory. In the git
+  * directory set the core.worktree setting to indicate where the worktree is.
+  * When `recurse_into_nested` is set, recurse into any nested submodules,
+  * connecting them as well.
+  */
+ extern void connect_work_tree_and_git_dir(const char *work_tree,
+                                         const char *git_dir,
+                                         int recurse_into_nested);
  extern void relocate_gitdir(const char *path,
                            const char *old_git_dir,
                            const char *new_git_dir);
diff --combined submodule-config.c
index 3f2075764feb53fc8c981aab39d16c6b191cb6d8,9abe9166d52796add52928c88f88a11141d21482..d87c3ff63a3cbc5bc0964499f84b7b4cebf04494
@@@ -9,7 -9,7 +9,7 @@@
  /*
   * submodule cache lookup structure
   * There is one shared set of 'struct submodule' entries which can be
 - * looked up by their sha1 blob id of the .gitmodule file and either
 + * looked up by their sha1 blob id of the .gitmodules file and either
   * using path or name as key.
   * for_path stores submodule entries with path as key
   * for_name stores submodule entries with name as key
@@@ -91,7 -91,7 +91,7 @@@ static void submodule_cache_clear(struc
        /*
         * We iterate over the name hash here to be symmetric with the
         * allocation of struct submodule entries. Each is allocated by
 -       * their .gitmodule blob sha1 and submodule name.
 +       * their .gitmodules blob sha1 and submodule name.
         */
        hashmap_iter_init(&cache->for_name, &iter);
        while ((entry = hashmap_iter_next(&iter)))
@@@ -520,7 -520,7 +520,7 @@@ static const struct submodule *config_f
        if (submodule)
                goto out;
  
 -      config = read_sha1_file(oid.hash, &type, &config_size);
 +      config = read_object_file(&oid, &type, &config_size);
        if (!config || type != OBJ_BLOB)
                goto out;
  
@@@ -619,31 -619,24 +619,24 @@@ static void gitmodules_read_check(struc
                repo_read_gitmodules(repo);
  }
  
- const struct submodule *submodule_from_name(const struct object_id *treeish_name,
+ const struct submodule *submodule_from_name(struct repository *r,
+                                           const struct object_id *treeish_name,
                const char *name)
  {
-       gitmodules_read_check(the_repository);
-       return config_from(the_repository->submodule_cache, treeish_name, name, lookup_name);
+       gitmodules_read_check(r);
+       return config_from(r->submodule_cache, treeish_name, name, lookup_name);
  }
  
- const struct submodule *submodule_from_path(const struct object_id *treeish_name,
+ const struct submodule *submodule_from_path(struct repository *r,
+                                           const struct object_id *treeish_name,
                const char *path)
  {
-       gitmodules_read_check(the_repository);
-       return config_from(the_repository->submodule_cache, treeish_name, path, lookup_path);
+       gitmodules_read_check(r);
+       return config_from(r->submodule_cache, treeish_name, path, lookup_path);
  }
  
- const struct submodule *submodule_from_cache(struct repository *repo,
-                                            const struct object_id *treeish_name,
-                                            const char *key)
+ void submodule_free(struct repository *r)
  {
-       gitmodules_read_check(repo);
-       return config_from(repo->submodule_cache, treeish_name,
-                          key, lookup_path);
- }
- void submodule_free(void)
- {
-       if (the_repository->submodule_cache)
-               submodule_cache_clear(the_repository->submodule_cache);
+       if (r->submodule_cache)
+               submodule_cache_clear(r->submodule_cache);
  }
diff --combined submodule.c
index 9a50168b2375d9cac306a22c8be4559dde114d95,53c45e49d0547dd2d0b52a7af3ad35ad510d0a91..74d35b25779f4f771cc9d9ababe3a75cb04ac81c
@@@ -96,7 -96,7 +96,7 @@@ int update_path_in_gitmodules(const cha
        if (is_gitmodules_unmerged(&the_index))
                die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
  
-       submodule = submodule_from_path(&null_oid, oldpath);
+       submodule = submodule_from_path(the_repository, &null_oid, oldpath);
        if (!submodule || !submodule->name) {
                warning(_("Could not find section in .gitmodules where path=%s"), oldpath);
                return -1;
@@@ -130,7 -130,7 +130,7 @@@ int remove_path_from_gitmodules(const c
        if (is_gitmodules_unmerged(&the_index))
                die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
  
-       submodule = submodule_from_path(&null_oid, path);
+       submodule = submodule_from_path(the_repository, &null_oid, path);
        if (!submodule || !submodule->name) {
                warning(_("Could not find section in .gitmodules where path=%s"), path);
                return -1;
@@@ -174,7 -174,8 +174,8 @@@ done
  void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
                                             const char *path)
  {
-       const struct submodule *submodule = submodule_from_path(&null_oid, path);
+       const struct submodule *submodule = submodule_from_path(the_repository,
+                                                               &null_oid, path);
        if (submodule) {
                const char *ignore;
                char *key;
@@@ -230,7 -231,7 +231,7 @@@ int is_submodule_active(struct reposito
        const struct string_list *sl;
        const struct submodule *module;
  
-       module = submodule_from_cache(repo, &null_oid, path);
+       module = submodule_from_path(repo, &null_oid, path);
  
        /* early return if there isn't a path->module mapping */
        if (!module)
@@@ -541,9 -542,9 +542,9 @@@ static void show_submodule_header(struc
  
  output_header:
        strbuf_addf(&sb, "Submodule %s ", path);
 -      strbuf_add_unique_abbrev(&sb, one->hash, DEFAULT_ABBREV);
 +      strbuf_add_unique_abbrev(&sb, one, DEFAULT_ABBREV);
        strbuf_addstr(&sb, (fast_backward || fast_forward) ? ".." : "...");
 -      strbuf_add_unique_abbrev(&sb, two->hash, DEFAULT_ABBREV);
 +      strbuf_add_unique_abbrev(&sb, two, DEFAULT_ABBREV);
        if (message)
                strbuf_addf(&sb, " %s\n", message);
        else
@@@ -591,7 -592,7 +592,7 @@@ void show_submodule_inline_diff(struct 
                struct object_id *one, struct object_id *two,
                unsigned dirty_submodule)
  {
 -      const struct object_id *old = the_hash_algo->empty_tree, *new = the_hash_algo->empty_tree;
 +      const struct object_id *old_oid = the_hash_algo->empty_tree, *new_oid = the_hash_algo->empty_tree;
        struct commit *left = NULL, *right = NULL;
        struct commit_list *merge_bases = NULL;
        struct child_process cp = CHILD_PROCESS_INIT;
                goto done;
  
        if (left)
 -              old = one;
 +              old_oid = one;
        if (right)
 -              new = two;
 +              new_oid = two;
  
        cp.git_cmd = 1;
        cp.dir = path;
                argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
                                 o->b_prefix, path);
        }
 -      argv_array_push(&cp.args, oid_to_hex(old));
 +      argv_array_push(&cp.args, oid_to_hex(old_oid));
        /*
         * If the submodule has modified content, we will diff against the
         * work tree, under the assumption that the user has asked for the
         * haven't yet been committed to the submodule yet.
         */
        if (!(dirty_submodule & DIRTY_SUBMODULE_MODIFIED))
 -              argv_array_push(&cp.args, oid_to_hex(new));
 +              argv_array_push(&cp.args, oid_to_hex(new_oid));
  
        prepare_submodule_repo_env(&cp.env_array);
        if (start_command(&cp))
@@@ -674,7 -675,7 +675,7 @@@ const struct submodule *submodule_from_
        if (!should_update_submodules())
                return NULL;
  
-       return submodule_from_path(&null_oid, ce->name);
+       return submodule_from_path(the_repository, &null_oid, ce->name);
  }
  
  static struct oid_array *submodule_commits(struct string_list *submodules,
@@@ -731,13 -732,14 +732,14 @@@ static void collect_changed_submodules_
                if (!S_ISGITLINK(p->two->mode))
                        continue;
  
-               submodule = submodule_from_path(commit_oid, p->two->path);
+               submodule = submodule_from_path(the_repository,
+                                               commit_oid, p->two->path);
                if (submodule)
                        name = submodule->name;
                else {
                        name = default_name_or_path(p->two->path);
                        /* make sure name does not collide with existing one */
-                       submodule = submodule_from_name(commit_oid, name);
+                       submodule = submodule_from_name(the_repository, commit_oid, name);
                        if (submodule) {
                                warning("Submodule in commit %s at path: "
                                        "'%s' collides with a submodule named "
@@@ -818,7 -820,7 +820,7 @@@ static int check_has_commit(const struc
  {
        struct has_commit_data *cb = data;
  
 -      enum object_type type = sha1_object_info(oid->hash, NULL);
 +      enum object_type type = oid_object_info(oid, NULL);
  
        switch (type) {
        case OBJ_COMMIT:
                return 0;
        default:
                die(_("submodule entry '%s' (%s) is a %s, not a commit"),
 -                  cb->path, oid_to_hex(oid), typename(type));
 +                  cb->path, oid_to_hex(oid), type_name(type));
        }
  }
  
@@@ -945,7 -947,7 +947,7 @@@ int find_unpushed_submodules(struct oid
                const struct submodule *submodule;
                const char *path = NULL;
  
-               submodule = submodule_from_name(&null_oid, name->string);
+               submodule = submodule_from_name(the_repository, &null_oid, name->string);
                if (submodule)
                        path = submodule->path;
                else
@@@ -1113,7 -1115,7 +1115,7 @@@ static void calculate_changed_submodule
        const struct string_list_item *name;
  
        /* No need to check if there are no submodules configured */
-       if (!submodule_from_path(NULL, NULL))
+       if (!submodule_from_path(the_repository, NULL, NULL))
                return;
  
        argv_array_push(&argv, "--"); /* argv[0] program name */
                const struct submodule *submodule;
                const char *path = NULL;
  
-               submodule = submodule_from_name(&null_oid, name->string);
+               submodule = submodule_from_name(the_repository, &null_oid, name->string);
                if (submodule)
                        path = submodule->path;
                else
@@@ -1162,7 -1164,7 +1164,7 @@@ int submodule_touches_in_range(struct o
        int ret;
  
        /* No need to check if there are no submodules configured */
-       if (!submodule_from_path(NULL, NULL))
+       if (!submodule_from_path(the_repository, NULL, NULL))
                return 0;
  
        argv_array_push(&args, "--"); /* args[0] program name */
@@@ -1234,7 -1236,7 +1236,7 @@@ static int get_next_submodule(struct ch
                if (!S_ISGITLINK(ce->ce_mode))
                        continue;
  
-               submodule = submodule_from_cache(spf->r, &null_oid, ce->name);
+               submodule = submodule_from_path(spf->r, &null_oid, ce->name);
                if (!submodule) {
                        const char *name = default_name_or_path(ce->name);
                        if (name) {
@@@ -1579,8 -1581,8 +1581,8 @@@ static void submodule_reset_index(cons
   * pass NULL for old or new respectively.
   */
  int submodule_move_head(const char *path,
 -                       const char *old,
 -                       const char *new,
 +                       const char *old_head,
 +                       const char *new_head,
                         unsigned flags)
  {
        int ret = 0;
        else
                error_code_ptr = NULL;
  
 -      if (old && !is_submodule_populated_gently(path, error_code_ptr))
 +      if (old_head && !is_submodule_populated_gently(path, error_code_ptr))
                return 0;
  
-       sub = submodule_from_path(&null_oid, path);
+       sub = submodule_from_path(the_repository, &null_oid, path);
  
        if (!sub)
                die("BUG: could not get submodule information for '%s'", path);
  
 -      if (old && !(flags & SUBMODULE_MOVE_HEAD_FORCE)) {
 +      if (old_head && !(flags & SUBMODULE_MOVE_HEAD_FORCE)) {
                /* Check if the submodule has a dirty index. */
                if (submodule_has_dirty_index(sub))
                        return error(_("submodule '%s' has dirty index"), path);
        }
  
        if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
 -              if (old) {
 +              if (old_head) {
                        if (!submodule_uses_gitfile(path))
                                absorb_git_dir_into_superproject("", path,
                                        ABSORB_GITDIR_RECURSE_SUBMODULES);
                } else {
                        char *gitdir = xstrfmt("%s/modules/%s",
                                    get_git_common_dir(), sub->name);
-                       connect_work_tree_and_git_dir(path, gitdir);
+                       connect_work_tree_and_git_dir(path, gitdir, 0);
                        free(gitdir);
  
                        /* make sure the index is clean as well */
                        submodule_reset_index(path);
                }
  
 -              if (old && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
 +              if (old_head && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
                        char *gitdir = xstrfmt("%s/modules/%s",
                                    get_git_common_dir(), sub->name);
-                       connect_work_tree_and_git_dir(path, gitdir);
+                       connect_work_tree_and_git_dir(path, gitdir, 1);
                        free(gitdir);
                }
        }
                argv_array_push(&cp.args, "-m");
  
        if (!(flags & SUBMODULE_MOVE_HEAD_FORCE))
 -              argv_array_push(&cp.args, old ? old : EMPTY_TREE_SHA1_HEX);
 +              argv_array_push(&cp.args, old_head ? old_head : EMPTY_TREE_SHA1_HEX);
  
 -      argv_array_push(&cp.args, new ? new : EMPTY_TREE_SHA1_HEX);
 +      argv_array_push(&cp.args, new_head ? new_head : EMPTY_TREE_SHA1_HEX);
  
        if (run_command(&cp)) {
                ret = -1;
        }
  
        if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
 -              if (new) {
 +              if (new_head) {
                        child_process_init(&cp);
                        /* also set the HEAD accordingly */
                        cp.git_cmd = 1;
  
                        prepare_submodule_repo_env(&cp.env_array);
                        argv_array_pushl(&cp.args, "update-ref", "HEAD",
 -                                       "--no-deref", new, NULL);
 +                                       "--no-deref", new_head, NULL);
  
                        if (run_command(&cp)) {
                                ret = -1;
@@@ -1886,7 -1888,7 +1888,7 @@@ static void relocate_single_git_dir_int
  
        real_old_git_dir = real_pathdup(old_git_dir, 1);
  
-       sub = submodule_from_path(&null_oid, path);
+       sub = submodule_from_path(the_repository, &null_oid, path);
        if (!sub)
                die(_("could not lookup name for submodule '%s'"), path);
  
@@@ -1942,11 -1944,11 +1944,11 @@@ void absorb_git_dir_into_superproject(c
                * superproject did not rewrite the git file links yet,
                * fix it now.
                */
-               sub = submodule_from_path(&null_oid, path);
+               sub = submodule_from_path(the_repository, &null_oid, path);
                if (!sub)
                        die(_("could not lookup name for submodule '%s'"), path);
                connect_work_tree_and_git_dir(path,
-                       git_path("modules/%s", sub->name));
+                       git_path("modules/%s", sub->name), 0);
        } else {
                /* Is it already absorbed into the superprojects git dir? */
                char *real_sub_git_dir = real_pathdup(sub_git_dir, 1);
@@@ -2088,7 -2090,7 +2090,7 @@@ int submodule_to_gitdir(struct strbuf *
                strbuf_addstr(buf, git_dir);
        }
        if (!is_git_directory(buf->buf)) {
-               sub = submodule_from_path(&null_oid, submodule);
+               sub = submodule_from_path(the_repository, &null_oid, submodule);
                if (!sub) {
                        ret = -1;
                        goto cleanup;
diff --combined submodule.h
index 9589f131273d4f04605c8dbf7dcce05aaea606ad,b6130e6287843baaf073d1fbe0b7a2ba2e8c37a1..e5526f6aaab93f85d279e89fc33b8e2e8740c32a
@@@ -105,7 -105,6 +105,6 @@@ extern int push_unpushed_submodules(str
                                    const char **refspec, int refspec_nr,
                                    const struct string_list *push_options,
                                    int dry_run);
- extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
  /*
   * Given a submodule path (as in the index), return the repository
   * path of that submodule in 'buf'. Return -1 on error or when the
@@@ -117,7 -116,7 +116,7 @@@ int submodule_to_gitdir(struct strbuf *
  #define SUBMODULE_MOVE_HEAD_FORCE   (1<<1)
  extern int submodule_move_head(const char *path,
                               const char *old,
 -                             const char *new,
 +                             const char *new_head,
                               unsigned flags);
  
  /*
index 5c6e4b010d61d2076f08e7077aa5ccbe6db665fc,e044871ceec012b42b6617ab8330c2324a25f42c..e2692746dfdb0e6a5c3b1c748124059bcbdd5fae
@@@ -1,4 -1,3 +1,4 @@@
 +#include "test-tool.h"
  #include "cache.h"
  #include "config.h"
  #include "submodule-config.h"
@@@ -11,7 -10,7 +11,7 @@@ static void die_usage(int argc, const c
        exit(1);
  }
  
 -int cmd_main(int argc, const char **argv)
 +int cmd__submodule_config(int argc, const char **argv)
  {
        const char **arg = argv;
        int my_argc = argc;
                        die_usage(argc, argv, "Commit not found.");
  
                if (lookup_name) {
-                       submodule = submodule_from_name(&commit_oid, path_or_name);
+                       submodule = submodule_from_name(the_repository,
+                                                       &commit_oid, path_or_name);
                } else
-                       submodule = submodule_from_path(&commit_oid, path_or_name);
+                       submodule = submodule_from_path(the_repository,
+                                                       &commit_oid, path_or_name);
                if (!submodule)
                        die_usage(argc, argv, "Submodule not found.");
  
@@@ -65,7 -66,7 +67,7 @@@
                arg += 2;
        }
  
-       submodule_free();
+       submodule_free(the_repository);
  
        return 0;
  }
diff --combined t/t7001-mv.sh
index e96cbdb105824bf7edaf5106113b8c534765abff,bfe2c427f1359e44a12c80460f6cc4dca08be8da..cc3fd2baf2b80817ffc39ab67a988dcf31dbc537
@@@ -21,8 -21,8 +21,8 @@@ test_expect_success 
  
  test_expect_success \
      'checking the commit' \
 -    'git diff-tree -r -M --name-status  HEAD^ HEAD | \
 -    grep "^R100..*path0/COPYING..*path1/COPYING"'
 +    'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
 +    grep "^R100..*path0/COPYING..*path1/COPYING" actual'
  
  test_expect_success \
      'moving the file back into subdirectory' \
@@@ -35,14 -35,8 +35,14 @@@ test_expect_success 
  
  test_expect_success \
      'checking the commit' \
 -    'git diff-tree -r -M --name-status  HEAD^ HEAD | \
 -    grep "^R100..*path1/COPYING..*path0/COPYING"'
 +    'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
 +    grep "^R100..*path1/COPYING..*path0/COPYING" actual'
 +
 +test_expect_success \
 +    'mv --dry-run does not move file' \
 +    'git mv -n path0/COPYING MOVED &&
 +     test -f path0/COPYING &&
 +     test ! -f MOVED'
  
  test_expect_success \
      'checking -k on non-existing file' \
@@@ -122,9 -116,10 +122,9 @@@ test_expect_success 
  
  test_expect_success \
      'checking the commit' \
 -    'git diff-tree -r -M --name-status  HEAD^ HEAD | \
 -     grep "^R100..*path0/COPYING..*path2/COPYING" &&
 -     git diff-tree -r -M --name-status  HEAD^ HEAD | \
 -     grep "^R100..*path0/README..*path2/README"'
 +    'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
 +     grep "^R100..*path0/COPYING..*path2/COPYING" actual &&
 +     grep "^R100..*path0/README..*path2/README" actual'
  
  test_expect_success \
      'succeed when source is a prefix of destination' \
@@@ -140,9 -135,10 +140,9 @@@ test_expect_success 
  
  test_expect_success \
      'checking the commit' \
 -    'git diff-tree -r -M --name-status  HEAD^ HEAD | \
 -     grep "^R100..*path2/COPYING..*path1/path2/COPYING" &&
 -     git diff-tree -r -M --name-status  HEAD^ HEAD | \
 -     grep "^R100..*path2/README..*path1/path2/README"'
 +    'git diff-tree -r -M --name-status  HEAD^ HEAD >actual &&
 +     grep "^R100..*path2/COPYING..*path1/path2/COPYING" actual &&
 +     grep "^R100..*path2/README..*path1/path2/README" actual'
  
  test_expect_success \
      'do not move directory over existing directory' \
@@@ -495,7 -491,7 +495,7 @@@ test_expect_success 'moving a submodul
        test_cmp expect actual
  '
  
- test_expect_failure 'moving nested submodules' '
+ test_expect_success 'moving nested submodules' '
        git commit -am "cleanup commit" &&
        mkdir sub_nested_nested &&
        (cd sub_nested_nested &&
diff --combined unpack-trees.c
index e73745051e505934b44be216b1d80a6c053c96de,3a6a28e794cd311d0e39068840e0cb70d53ceaf5..dec37ad1e7409aa6814cd22c914e05bcd9babf1d
@@@ -195,10 -195,10 +195,10 @@@ static int do_add_entry(struct unpack_t
  static struct cache_entry *dup_entry(const struct cache_entry *ce)
  {
        unsigned int size = ce_size(ce);
 -      struct cache_entry *new = xmalloc(size);
 +      struct cache_entry *new_entry = xmalloc(size);
  
 -      memcpy(new, ce, size);
 -      return new;
 +      memcpy(new_entry, ce, size);
 +      return new_entry;
  }
  
  static void add_entry(struct unpack_trees_options *o,
@@@ -290,7 -290,7 +290,7 @@@ static void load_gitmodules_file(struc
                if (!state && ce->ce_flags & CE_WT_REMOVE) {
                        repo_read_gitmodules(the_repository);
                } else if (state && (ce->ce_flags & CE_UPDATE)) {
-                       submodule_free();
+                       submodule_free(the_repository);
                        checkout_entry(ce, state, NULL);
                        repo_read_gitmodules(the_repository);
                }
@@@ -391,7 -391,6 +391,7 @@@ static int check_updates(struct unpack_
                        fetch_objects(repository_format_partial_clone,
                                      &to_fetch);
                fetch_if_missing = fetch_if_missing_store;
 +              oid_array_clear(&to_fetch);
        }
        for (i = 0; i < index->cache_nr; i++) {
                struct cache_entry *ce = index->cache[i];
@@@ -1529,7 -1528,7 +1529,7 @@@ static void invalidate_ce_path(const st
        if (!ce)
                return;
        cache_tree_invalidate_path(o->src_index, ce->name);
 -      untracked_cache_invalidate_path(o->src_index, ce->name);
 +      untracked_cache_invalidate_path(o->src_index, ce->name, 1);
  }
  
  /*