Merge branch 'bw/grep-recurse-submodules'
authorJunio C Hamano <gitster@pobox.com>
Wed, 18 Jan 2017 23:12:11 +0000 (15:12 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 18 Jan 2017 23:12:11 +0000 (15:12 -0800)
"git grep" has been taught to optionally recurse into submodules.

* bw/grep-recurse-submodules:
grep: search history of moved submodules
grep: enable recurse-submodules to work on <tree> objects
grep: optionally recurse into submodules
grep: add submodules as a grep source type
submodules: load gitmodules file from commit sha1
submodules: add helper to determine if a submodule is initialized
submodules: add helper to determine if a submodule is populated
real_path: canonicalize directory separators in root parts
real_path: have callers use real_pathdup and strbuf_realpath
real_path: create real_pathdup
real_path: convert real_path_internal to strbuf_realpath
real_path: resolve symlinks by hand

1  2 
cache.h
config.c
environment.c
git.c
sha1_file.c
submodule-config.c
submodule-config.h
submodule.c
submodule.h
transport.c
worktree.c
diff --combined cache.h
index 1b67f078ddf7473cbde1351dc11405ec239e1373,de237cab6b760c74f9858952473465a5875e4ac5..c74df701276e92aaae71dee36da96197b2dc3316
+++ b/cache.h
@@@ -670,7 -670,7 +670,7 @@@ extern const char *git_attributes_file
  extern const char *git_hooks_path;
  extern int zlib_compression_level;
  extern int core_compression_level;
 -extern int core_compression_seen;
 +extern int pack_compression_level;
  extern size_t packed_git_window_size;
  extern size_t packed_git_limit;
  extern size_t delta_base_cache_limit;
@@@ -1064,8 -1064,11 +1064,11 @@@ static inline int is_absolute_path(cons
        return is_dir_sep(path[0]) || has_dos_drive_prefix(path);
  }
  int is_directory(const char *);
+ char *strbuf_realpath(struct strbuf *resolved, const char *path,
+                     int die_on_error);
  const char *real_path(const char *path);
  const char *real_path_if_valid(const char *path);
+ char *real_pathdup(const char *path);
  const char *absolute_path(const char *path);
  const char *remove_leading_path(const char *in, const char *prefix);
  const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
@@@ -1125,8 -1128,7 +1128,8 @@@ extern int write_sha1_file(const void *
  extern int hash_sha1_file_literally(const void *buf, unsigned long len, const char *type, unsigned char *sha1, unsigned flags);
  extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
  extern int force_object_loose(const unsigned char *sha1, time_t mtime);
 -extern int git_open(const char *name);
 +extern int git_open_cloexec(const char *name, int flags);
 +#define git_open(name) git_open_cloexec(name, O_RDONLY)
  extern void *map_sha1_file(const unsigned char *sha1, unsigned long *size);
  extern int unpack_sha1_header(git_zstream *stream, unsigned char *map, unsigned long mapsize, void *buffer, unsigned long bufsiz);
  extern int parse_sha1_header(const char *hdr, unsigned long *sizep);
@@@ -1691,6 -1693,8 +1694,8 @@@ extern int git_default_config(const cha
  extern int git_config_from_file(config_fn_t fn, const char *, void *);
  extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
                                        const char *name, const char *buf, size_t len, void *data);
+ extern int git_config_from_blob_sha1(config_fn_t fn, const char *name,
+                                    const unsigned char *sha1, void *data);
  extern void git_config_push_parameter(const char *text);
  extern int git_config_from_parameters(config_fn_t fn, void *data);
  extern void git_config(config_fn_t fn, void *);
diff --combined config.c
index 617b2e3cf4a9dcb61283ea797166d2bda812de6f,4d78e7227c4f5d2337c62a53e19aeba2f4e6c5f7..b680f79732aa867d39a2e10ac889b5b2abfb1e7f
+++ b/config.c
@@@ -66,8 -66,6 +66,8 @@@ static struct key_value_info *current_c
   */
  static enum config_scope current_parsing_scope;
  
 +static int core_compression_seen;
 +static int pack_compression_seen;
  static int zlib_compression_seen;
  
  /*
@@@ -836,16 -834,10 +836,16 @@@ static int git_default_core_config(cons
        }
  
        if (!strcmp(var, "core.abbrev")) {
 -              int abbrev = git_config_int(var, value);
 -              if (abbrev < minimum_abbrev || abbrev > 40)
 -                      return -1;
 -              default_abbrev = abbrev;
 +              if (!value)
 +                      return config_error_nonbool(var);
 +              if (!strcasecmp(value, "auto"))
 +                      default_abbrev = -1;
 +              else {
 +                      int abbrev = git_config_int(var, value);
 +                      if (abbrev < minimum_abbrev || abbrev > 40)
 +                              return error("abbrev length out of range: %d", abbrev);
 +                      default_abbrev = abbrev;
 +              }
                return 0;
        }
  
                core_compression_seen = 1;
                if (!zlib_compression_seen)
                        zlib_compression_level = level;
 +              if (!pack_compression_seen)
 +                      pack_compression_level = level;
                return 0;
        }
  
@@@ -1135,18 -1125,6 +1135,18 @@@ int git_default_config(const char *var
                pack_size_limit_cfg = git_config_ulong(var, value);
                return 0;
        }
 +
 +      if (!strcmp(var, "pack.compression")) {
 +              int level = git_config_int(var, value);
 +              if (level == -1)
 +                      level = Z_DEFAULT_COMPRESSION;
 +              else if (level < 0 || level > Z_BEST_COMPRESSION)
 +                      die(_("bad pack compression level %d"), level);
 +              pack_compression_level = level;
 +              pack_compression_seen = 1;
 +              return 0;
 +      }
 +
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
  }
@@@ -1236,10 -1214,10 +1236,10 @@@ int git_config_from_mem(config_fn_t fn
        return do_config_from(&top, fn, data);
  }
  
static int git_config_from_blob_sha1(config_fn_t fn,
-                                    const char *name,
-                                    const unsigned char *sha1,
-                                    void *data)
+ int git_config_from_blob_sha1(config_fn_t fn,
+                             const char *name,
+                             const unsigned char *sha1,
+                             void *data)
  {
        enum object_type type;
        char *buf;
@@@ -2216,12 -2194,7 +2216,12 @@@ int git_config_set_multivar_in_file_gen
                        goto out_free;
                }
  
 -              fstat(in_fd, &st);
 +              if (fstat(in_fd, &st) == -1) {
 +                      error_errno(_("fstat on %s failed"), config_filename);
 +                      ret = CONFIG_INVALID_FILE;
 +                      goto out_free;
 +              }
 +
                contents_sz = xsize_t(st.st_size);
                contents = xmmap_gently(NULL, contents_sz, PROT_READ,
                                        MAP_PRIVATE, in_fd, 0);
@@@ -2423,7 -2396,7 +2423,7 @@@ int git_config_rename_section_in_file(c
  
        if (new_name && !section_name_is_ok(new_name)) {
                ret = error("invalid section name: %s", new_name);
 -              goto out;
 +              goto out_no_rollback;
        }
  
        if (!config_filename)
  
        if (!(config_file = fopen(config_filename, "rb"))) {
                /* no config file means nothing to rename, no error */
 -              goto unlock_and_out;
 +              goto commit_and_out;
        }
  
 -      fstat(fileno(config_file), &st);
 +      if (fstat(fileno(config_file), &st) == -1) {
 +              ret = error_errno(_("fstat on %s failed"), config_filename);
 +              goto out;
 +      }
  
        if (chmod(get_lock_file_path(lock), st.st_mode & 07777) < 0) {
                ret = error_errno("chmod on %s failed",
                }
        }
        fclose(config_file);
 -unlock_and_out:
 +commit_and_out:
        if (commit_lock_file(lock) < 0)
                ret = error_errno("could not write config file %s",
                                  config_filename);
  out:
 +      rollback_lock_file(lock);
 +out_no_rollback:
        free(filename_buf);
        return ret;
  }
diff --combined environment.c
index 4bce3eebfafa45d5781520781693029ffb317985,9b943d2d5bbc109cabd24f0f2efea7a5e79875a5..8a83101d04240b1b5b1471e68c810f822f68269d
@@@ -34,7 -34,7 +34,7 @@@ const char *git_attributes_file
  const char *git_hooks_path;
  int zlib_compression_level = Z_BEST_SPEED;
  int core_compression_level;
 -int core_compression_seen;
 +int pack_compression_level = Z_DEFAULT_COMPRESSION;
  int fsync_object_files;
  size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
  size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
@@@ -259,7 -259,7 +259,7 @@@ void set_git_work_tree(const char *new_
                return;
        }
        git_work_tree_initialized = 1;
-       work_tree = xstrdup(real_path(new_work_tree));
+       work_tree = real_pathdup(new_work_tree);
  }
  
  const char *get_git_work_tree(void)
diff --combined git.c
index bbaa949e9cea975b0b5a8ef1799249bf0445dcaa,c95d3e30da8951f1a7c206cb11793cb113db6f9f..e4714aadb3e6f38baec6afaee493c15590875684
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -434,7 -434,7 +434,7 @@@ static struct cmd_struct commands[] = 
        { "fsck-objects", cmd_fsck, RUN_SETUP },
        { "gc", cmd_gc, RUN_SETUP },
        { "get-tar-commit-id", cmd_get_tar_commit_id },
-       { "grep", cmd_grep, RUN_SETUP_GENTLY },
+       { "grep", cmd_grep, RUN_SETUP_GENTLY | SUPPORT_SUPER_PREFIX },
        { "hash-object", cmd_hash_object },
        { "help", cmd_help },
        { "index-pack", cmd_index_pack, RUN_SETUP_GENTLY },
        { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
        { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
        { "stripspace", cmd_stripspace },
 -      { "submodule--helper", cmd_submodule__helper, RUN_SETUP },
 +      { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX},
        { "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
        { "tag", cmd_tag, RUN_SETUP },
        { "unpack-file", cmd_unpack_file, RUN_SETUP },
diff --combined sha1_file.c
index 1eb47f61131a81b587dc4fe80e1b175f37ba0540,6a03cc39342cde47adee9664786fcf3af93c7f0d..b5e827ac9e716db9fad69960058f1bf2fa50c9d3
  #include "mru.h"
  #include "list.h"
  #include "mergesort.h"
 -
 -#ifndef O_NOATIME
 -#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
 -#define O_NOATIME 01000000
 -#else
 -#define O_NOATIME 0
 -#endif
 -#endif
 +#include "quote.h"
  
  #define SZ_FMT PRIuMAX
  static inline uintmax_t sz_fmt(size_t s) { return s; }
@@@ -284,7 -291,7 +284,7 @@@ static int link_alt_odb_entry(const cha
        struct strbuf pathbuf = STRBUF_INIT;
  
        if (!is_absolute_path(entry) && relative_base) {
-               strbuf_addstr(&pathbuf, real_path(relative_base));
+               strbuf_realpath(&pathbuf, relative_base, 1);
                strbuf_addch(&pathbuf, '/');
        }
        strbuf_addstr(&pathbuf, entry);
        return 0;
  }
  
 +static const char *parse_alt_odb_entry(const char *string,
 +                                     int sep,
 +                                     struct strbuf *out)
 +{
 +      const char *end;
 +
 +      strbuf_reset(out);
 +
 +      if (*string == '#') {
 +              /* comment; consume up to next separator */
 +              end = strchrnul(string, sep);
 +      } else if (*string == '"' && !unquote_c_style(out, string, &end)) {
 +              /*
 +               * quoted path; unquote_c_style has copied the
 +               * data for us and set "end". Broken quoting (e.g.,
 +               * an entry that doesn't end with a quote) falls
 +               * back to the unquoted case below.
 +               */
 +      } else {
 +              /* normal, unquoted path */
 +              end = strchrnul(string, sep);
 +              strbuf_add(out, string, end - string);
 +      }
 +
 +      if (*end)
 +              end++;
 +      return end;
 +}
 +
  static void link_alt_odb_entries(const char *alt, int len, int sep,
                                 const char *relative_base, int depth)
  {
 -      struct string_list entries = STRING_LIST_INIT_NODUP;
 -      char *alt_copy;
 -      int i;
        struct strbuf objdirbuf = STRBUF_INIT;
 +      struct strbuf entry = STRBUF_INIT;
  
        if (depth > 5) {
                error("%s: ignoring alternate object stores, nesting too deep.",
                die("unable to normalize object directory: %s",
                    objdirbuf.buf);
  
 -      alt_copy = xmemdupz(alt, len);
 -      string_list_split_in_place(&entries, alt_copy, sep, -1);
 -      for (i = 0; i < entries.nr; i++) {
 -              const char *entry = entries.items[i].string;
 -              if (entry[0] == '\0' || entry[0] == '#')
 +      while (*alt) {
 +              alt = parse_alt_odb_entry(alt, sep, &entry);
 +              if (!entry.len)
                        continue;
 -              link_alt_odb_entry(entry, relative_base, depth, objdirbuf.buf);
 +              link_alt_odb_entry(entry.buf, relative_base, depth, objdirbuf.buf);
        }
 -      string_list_clear(&entries, 0);
 -      free(alt_copy);
 +      strbuf_release(&entry);
        strbuf_release(&objdirbuf);
  }
  
@@@ -1603,31 -1586,31 +1603,31 @@@ int check_sha1_signature(const unsigne
        return hashcmp(sha1, real_sha1) ? -1 : 0;
  }
  
 -int git_open(const char *name)
 +int git_open_cloexec(const char *name, int flags)
  {
 -      static int sha1_file_open_flag = O_NOATIME | O_CLOEXEC;
 -
 -      for (;;) {
 -              int fd;
 -
 -              errno = 0;
 -              fd = open(name, O_RDONLY | sha1_file_open_flag);
 -              if (fd >= 0)
 -                      return fd;
 +      int fd;
 +      static int o_cloexec = O_CLOEXEC;
  
 +      fd = open(name, flags | o_cloexec);
 +      if ((o_cloexec & O_CLOEXEC) && fd < 0 && errno == EINVAL) {
                /* Try again w/o O_CLOEXEC: the kernel might not support it */
 -              if ((sha1_file_open_flag & O_CLOEXEC) && errno == EINVAL) {
 -                      sha1_file_open_flag &= ~O_CLOEXEC;
 -                      continue;
 -              }
 +              o_cloexec &= ~O_CLOEXEC;
 +              fd = open(name, flags | o_cloexec);
 +      }
  
 -              /* Might the failure be due to O_NOATIME? */
 -              if (errno != ENOENT && (sha1_file_open_flag & O_NOATIME)) {
 -                      sha1_file_open_flag &= ~O_NOATIME;
 -                      continue;
 +#if defined(F_GETFL) && defined(F_SETFL) && defined(FD_CLOEXEC)
 +      {
 +              static int fd_cloexec = FD_CLOEXEC;
 +
 +              if (!o_cloexec && 0 <= fd && fd_cloexec) {
 +                      /* Opened w/o O_CLOEXEC?  try with fcntl(2) to add it */
 +                      int flags = fcntl(fd, F_GETFL);
 +                      if (fcntl(fd, F_SETFL, flags | fd_cloexec))
 +                              fd_cloexec = 0;
                }
 -              return -1;
        }
 +#endif
 +      return fd;
  }
  
  static int stat_sha1_file(const unsigned char *sha1, struct stat *st)
diff --combined submodule-config.c
index ec13ab5a3dd82ac90c1c83d9e07dc8c045a992d2,8b9a2ef2888c3f12563b9392b4254905211e5aa5..4bf50f398a5bd705531e32f9329c0e3b7e630506
@@@ -263,12 -263,12 +263,12 @@@ int parse_push_recurse_submodules_arg(c
        return parse_push_recurse(opt, arg, 1);
  }
  
 -static void warn_multiple_config(const unsigned char *commit_sha1,
 +static void warn_multiple_config(const unsigned char *treeish_name,
                                 const char *name, const char *option)
  {
        const char *commit_string = "WORKTREE";
 -      if (commit_sha1)
 -              commit_string = sha1_to_hex(commit_sha1);
 +      if (treeish_name)
 +              commit_string = sha1_to_hex(treeish_name);
        warning("%s:.gitmodules, multiple configurations found for "
                        "'submodule.%s.%s'. Skipping second one!",
                        commit_string, name, option);
  
  struct parse_config_parameter {
        struct submodule_cache *cache;
 -      const unsigned char *commit_sha1;
 +      const unsigned char *treeish_name;
        const unsigned char *gitmodules_sha1;
        int overwrite;
  };
@@@ -300,7 -300,7 +300,7 @@@ static int parse_config(const char *var
                if (!value)
                        ret = config_error_nonbool(var);
                else if (!me->overwrite && submodule->path)
 -                      warn_multiple_config(me->commit_sha1, submodule->name,
 +                      warn_multiple_config(me->treeish_name, submodule->name,
                                        "path");
                else {
                        if (submodule->path)
                int die_on_error = is_null_sha1(me->gitmodules_sha1);
                if (!me->overwrite &&
                    submodule->fetch_recurse != RECURSE_SUBMODULES_NONE)
 -                      warn_multiple_config(me->commit_sha1, submodule->name,
 +                      warn_multiple_config(me->treeish_name, submodule->name,
                                        "fetchrecursesubmodules");
                else
                        submodule->fetch_recurse = parse_fetch_recurse(
                if (!value)
                        ret = config_error_nonbool(var);
                else if (!me->overwrite && submodule->ignore)
 -                      warn_multiple_config(me->commit_sha1, submodule->name,
 +                      warn_multiple_config(me->treeish_name, submodule->name,
                                        "ignore");
                else if (strcmp(value, "untracked") &&
                         strcmp(value, "dirty") &&
                if (!value) {
                        ret = config_error_nonbool(var);
                } else if (!me->overwrite && submodule->url) {
 -                      warn_multiple_config(me->commit_sha1, submodule->name,
 +                      warn_multiple_config(me->treeish_name, submodule->name,
                                        "url");
                } else {
                        free((void *) submodule->url);
                        ret = config_error_nonbool(var);
                else if (!me->overwrite &&
                         submodule->update_strategy.type != SM_UPDATE_UNSPECIFIED)
 -                      warn_multiple_config(me->commit_sha1, submodule->name,
 +                      warn_multiple_config(me->treeish_name, submodule->name,
                                             "update");
                else if (parse_submodule_update_strategy(value,
                         &submodule->update_strategy) < 0)
                                die(_("invalid value for %s"), var);
        } else if (!strcmp(item.buf, "shallow")) {
                if (!me->overwrite && submodule->recommend_shallow != -1)
 -                      warn_multiple_config(me->commit_sha1, submodule->name,
 +                      warn_multiple_config(me->treeish_name, submodule->name,
                                             "shallow");
                else
                        submodule->recommend_shallow =
                                git_config_bool(var, value);
        } else if (!strcmp(item.buf, "branch")) {
                if (!me->overwrite && submodule->branch)
 -                      warn_multiple_config(me->commit_sha1, submodule->name,
 +                      warn_multiple_config(me->treeish_name, submodule->name,
                                             "branch");
                else {
                        free((void *)submodule->branch);
        return ret;
  }
  
- static int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
 -int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
 -                             unsigned char *gitmodules_sha1,
 -                             struct strbuf *rev)
++int gitmodule_sha1_from_commit(const unsigned char *treeish_name,
 +                                    unsigned char *gitmodules_sha1,
 +                                    struct strbuf *rev)
  {
        int ret = 0;
  
 -      if (is_null_sha1(commit_sha1)) {
 +      if (is_null_sha1(treeish_name)) {
                hashclr(gitmodules_sha1);
                return 1;
        }
  
 -      strbuf_addf(rev, "%s:.gitmodules", sha1_to_hex(commit_sha1));
 +      strbuf_addf(rev, "%s:.gitmodules", sha1_to_hex(treeish_name));
        if (get_sha1(rev->buf, gitmodules_sha1) >= 0)
                ret = 1;
  
   * revisions.
   */
  static const struct submodule *config_from(struct submodule_cache *cache,
 -              const unsigned char *commit_sha1, const char *key,
 +              const unsigned char *treeish_name, const char *key,
                enum lookup_type lookup_type)
  {
        struct strbuf rev = STRBUF_INIT;
         * return the first submodule. Can be used to check whether
         * there are any submodules parsed.
         */
 -      if (!commit_sha1 || !key) {
 +      if (!treeish_name || !key) {
                struct hashmap_iter iter;
                struct submodule_entry *entry;
  
                return entry->config;
        }
  
 -      if (!gitmodule_sha1_from_commit(commit_sha1, sha1, &rev))
 +      if (!gitmodule_sha1_from_commit(treeish_name, sha1, &rev))
                goto out;
  
        switch (lookup_type) {
  
        /* fill the submodule config into the cache */
        parameter.cache = cache;
 -      parameter.commit_sha1 = commit_sha1;
 +      parameter.treeish_name = treeish_name;
        parameter.gitmodules_sha1 = sha1;
        parameter.overwrite = 0;
        git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
@@@ -471,6 -471,18 +471,6 @@@ out
        return submodule;
  }
  
 -static const struct submodule *config_from_path(struct submodule_cache *cache,
 -              const unsigned char *commit_sha1, const char *path)
 -{
 -      return config_from(cache, commit_sha1, path, lookup_path);
 -}
 -
 -static const struct submodule *config_from_name(struct submodule_cache *cache,
 -              const unsigned char *commit_sha1, const char *name)
 -{
 -      return config_from(cache, commit_sha1, name, lookup_name);
 -}
 -
  static void ensure_cache_init(void)
  {
        if (is_cache_init)
@@@ -484,7 -496,7 +484,7 @@@ int parse_submodule_config_option(cons
  {
        struct parse_config_parameter parameter;
        parameter.cache = &the_submodule_cache;
 -      parameter.commit_sha1 = NULL;
 +      parameter.treeish_name = NULL;
        parameter.gitmodules_sha1 = null_sha1;
        parameter.overwrite = 1;
  
        return parse_config(var, value, &parameter);
  }
  
 -const struct submodule *submodule_from_name(const unsigned char *commit_sha1,
 +const struct submodule *submodule_from_name(const unsigned char *treeish_name,
                const char *name)
  {
        ensure_cache_init();
 -      return config_from_name(&the_submodule_cache, commit_sha1, name);
 +      return config_from(&the_submodule_cache, treeish_name, name, lookup_name);
  }
  
 -const struct submodule *submodule_from_path(const unsigned char *commit_sha1,
 +const struct submodule *submodule_from_path(const unsigned char *treeish_name,
                const char *path)
  {
        ensure_cache_init();
 -      return config_from_path(&the_submodule_cache, commit_sha1, path);
 +      return config_from(&the_submodule_cache, treeish_name, path, lookup_path);
  }
  
  void submodule_free(void)
diff --combined submodule-config.h
index 99df8e593cc7c274f88a73dfdd6524b7f661692c,78584ba6a715f38ebe5c30b7617c84afbc657fa0..70f19363fd8bf5b7f42e974dacdc3ed2cd58a96a
@@@ -25,10 -25,13 +25,13 @@@ struct submodule 
  int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
  int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
  int parse_submodule_config_option(const char *var, const char *value);
 -const struct submodule *submodule_from_name(const unsigned char *commit_sha1,
 +const struct submodule *submodule_from_name(const unsigned char *commit_or_tree,
                const char *name);
 -const struct submodule *submodule_from_path(const unsigned char *commit_sha1,
 +const struct submodule *submodule_from_path(const unsigned char *commit_or_tree,
                const char *path);
+ extern int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
+                                     unsigned char *gitmodules_sha1,
+                                     struct strbuf *rev);
  void submodule_free(void);
  
  #endif /* SUBMODULE_CONFIG_H */
diff --combined submodule.c
index 73521cdbb29abf7e3b1d9eb7293d16e94ec8ccce,260090898acf4701a96c51d13cf4834d107f5c55..f8fee3dfddd27b569b3b505877b569d30dcd656d
@@@ -14,7 -14,6 +14,7 @@@
  #include "blob.h"
  #include "thread-utils.h"
  #include "quote.h"
 +#include "worktree.h"
  
  static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
  static int parallel_jobs = 1;
@@@ -199,6 -198,56 +199,56 @@@ void gitmodules_config(void
        }
  }
  
+ void gitmodules_config_sha1(const unsigned char *commit_sha1)
+ {
+       struct strbuf rev = STRBUF_INIT;
+       unsigned char sha1[20];
+       if (gitmodule_sha1_from_commit(commit_sha1, sha1, &rev)) {
+               git_config_from_blob_sha1(submodule_config, rev.buf,
+                                         sha1, NULL);
+       }
+       strbuf_release(&rev);
+ }
+ /*
+  * Determine if a submodule has been initialized at a given 'path'
+  */
+ int is_submodule_initialized(const char *path)
+ {
+       int ret = 0;
+       const struct submodule *module = NULL;
+       module = submodule_from_path(null_sha1, path);
+       if (module) {
+               char *key = xstrfmt("submodule.%s.url", module->name);
+               char *value = NULL;
+               ret = !git_config_get_string(key, &value);
+               free(value);
+               free(key);
+       }
+       return ret;
+ }
+ /*
+  * Determine if a submodule has been populated at a given 'path'
+  */
+ int is_submodule_populated(const char *path)
+ {
+       int ret = 0;
+       char *gitdir = xstrfmt("%s/.git", path);
+       if (resolve_gitdir(gitdir))
+               ret = 1;
+       free(gitdir);
+       return ret;
+ }
  int parse_submodule_update_strategy(const char *value,
                struct submodule_update_strategy *dst)
  {
@@@ -501,67 -550,27 +551,67 @@@ static int has_remote(const char *refna
        return 1;
  }
  
 -static int submodule_needs_pushing(const char *path, const unsigned char sha1[20])
 +static int append_sha1_to_argv(const unsigned char sha1[20], void *data)
  {
 -      if (add_submodule_odb(path) || !lookup_commit_reference(sha1))
 +      struct argv_array *argv = data;
 +      argv_array_push(argv, sha1_to_hex(sha1));
 +      return 0;
 +}
 +
 +static int check_has_commit(const unsigned char sha1[20], void *data)
 +{
 +      int *has_commit = data;
 +
 +      if (!lookup_commit_reference(sha1))
 +              *has_commit = 0;
 +
 +      return 0;
 +}
 +
 +static int submodule_has_commits(const char *path, struct sha1_array *commits)
 +{
 +      int has_commit = 1;
 +
 +      if (add_submodule_odb(path))
 +              return 0;
 +
 +      sha1_array_for_each_unique(commits, check_has_commit, &has_commit);
 +      return has_commit;
 +}
 +
 +static int submodule_needs_pushing(const char *path, struct sha1_array *commits)
 +{
 +      if (!submodule_has_commits(path, commits))
 +              /*
 +               * NOTE: We do consider it safe to return "no" here. The
 +               * correct answer would be "We do not know" instead of
 +               * "No push needed", but it is quite hard to change
 +               * the submodule pointer without having the submodule
 +               * around. If a user did however change the submodules
 +               * without having the submodule around, this indicates
 +               * an expert who knows what they are doing or a
 +               * maintainer integrating work from other people. In
 +               * both cases it should be safe to skip this check.
 +               */
                return 0;
  
        if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
                struct child_process cp = CHILD_PROCESS_INIT;
 -              const char *argv[] = {"rev-list", NULL, "--not", "--remotes", "-n", "1" , NULL};
                struct strbuf buf = STRBUF_INIT;
                int needs_pushing = 0;
  
 -              argv[1] = sha1_to_hex(sha1);
 -              cp.argv = argv;
 +              argv_array_push(&cp.args, "rev-list");
 +              sha1_array_for_each_unique(commits, append_sha1_to_argv, &cp.args);
 +              argv_array_pushl(&cp.args, "--not", "--remotes", "-n", "1" , NULL);
 +
                prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
                cp.out = -1;
                cp.dir = path;
                if (start_command(&cp))
 -                      die("Could not run 'git rev-list %s --not --remotes -n 1' command in submodule %s",
 -                              sha1_to_hex(sha1), path);
 +                      die("Could not run 'git rev-list <commits> --not --remotes -n 1' command in submodule %s",
 +                                      path);
                if (strbuf_read(&buf, cp.out, 41))
                        needs_pushing = 1;
                finish_command(&cp);
        return 0;
  }
  
 +static struct sha1_array *submodule_commits(struct string_list *submodules,
 +                                          const char *path)
 +{
 +      struct string_list_item *item;
 +
 +      item = string_list_insert(submodules, path);
 +      if (item->util)
 +              return (struct sha1_array *) item->util;
 +
 +      /* NEEDSWORK: should we have sha1_array_init()? */
 +      item->util = xcalloc(1, sizeof(struct sha1_array));
 +      return (struct sha1_array *) item->util;
 +}
 +
  static void collect_submodules_from_diff(struct diff_queue_struct *q,
                                         struct diff_options *options,
                                         void *data)
  {
        int i;
 -      struct string_list *needs_pushing = data;
 +      struct string_list *submodules = data;
  
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
 +              struct sha1_array *commits;
                if (!S_ISGITLINK(p->two->mode))
                        continue;
 -              if (submodule_needs_pushing(p->two->path, p->two->oid.hash))
 -                      string_list_insert(needs_pushing, p->two->path);
 +              commits = submodule_commits(submodules, p->two->path);
 +              sha1_array_append(commits, p->two->oid.hash);
        }
  }
  
@@@ -616,63 -610,46 +666,63 @@@ static void find_unpushed_submodule_com
        diff_tree_combined_merge(commit, 1, &rev);
  }
  
 -int find_unpushed_submodules(unsigned char new_sha1[20],
 +static void free_submodules_sha1s(struct string_list *submodules)
 +{
 +      struct string_list_item *item;
 +      for_each_string_list_item(item, submodules)
 +              sha1_array_clear((struct sha1_array *) item->util);
 +      string_list_clear(submodules, 1);
 +}
 +
 +int find_unpushed_submodules(struct sha1_array *commits,
                const char *remotes_name, struct string_list *needs_pushing)
  {
        struct rev_info rev;
        struct commit *commit;
 -      const char *argv[] = {NULL, NULL, "--not", "NULL", NULL};
 -      int argc = ARRAY_SIZE(argv) - 1;
 -      char *sha1_copy;
 -
 -      struct strbuf remotes_arg = STRBUF_INIT;
 +      struct string_list submodules = STRING_LIST_INIT_DUP;
 +      struct string_list_item *submodule;
 +      struct argv_array argv = ARGV_ARRAY_INIT;
  
 -      strbuf_addf(&remotes_arg, "--remotes=%s", remotes_name);
        init_revisions(&rev, NULL);
 -      sha1_copy = xstrdup(sha1_to_hex(new_sha1));
 -      argv[1] = sha1_copy;
 -      argv[3] = remotes_arg.buf;
 -      setup_revisions(argc, argv, &rev, NULL);
 +
 +      /* argv.argv[0] will be ignored by setup_revisions */
 +      argv_array_push(&argv, "find_unpushed_submodules");
 +      sha1_array_for_each_unique(commits, append_sha1_to_argv, &argv);
 +      argv_array_push(&argv, "--not");
 +      argv_array_pushf(&argv, "--remotes=%s", remotes_name);
 +
 +      setup_revisions(argv.argc, argv.argv, &rev, NULL);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
  
        while ((commit = get_revision(&rev)) != NULL)
 -              find_unpushed_submodule_commits(commit, needs_pushing);
 +              find_unpushed_submodule_commits(commit, &submodules);
  
        reset_revision_walk();
 -      free(sha1_copy);
 -      strbuf_release(&remotes_arg);
 +      argv_array_clear(&argv);
 +
 +      for_each_string_list_item(submodule, &submodules) {
 +              struct sha1_array *commits = (struct sha1_array *) submodule->util;
 +
 +              if (submodule_needs_pushing(submodule->string, commits))
 +                      string_list_insert(needs_pushing, submodule->string);
 +      }
 +      free_submodules_sha1s(&submodules);
  
        return needs_pushing->nr;
  }
  
 -static int push_submodule(const char *path)
 +static int push_submodule(const char *path, int dry_run)
  {
        if (add_submodule_odb(path))
                return 1;
  
        if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
                struct child_process cp = CHILD_PROCESS_INIT;
 -              const char *argv[] = {"push", NULL};
 +              argv_array_push(&cp.args, "push");
 +              if (dry_run)
 +                      argv_array_push(&cp.args, "--dry-run");
  
 -              cp.argv = argv;
                prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
        return 1;
  }
  
 -int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name)
 +int push_unpushed_submodules(struct sha1_array *commits,
 +                           const char *remotes_name,
 +                           int dry_run)
  {
        int i, ret = 1;
        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
  
 -      if (!find_unpushed_submodules(new_sha1, remotes_name, &needs_pushing))
 +      if (!find_unpushed_submodules(commits, remotes_name, &needs_pushing))
                return 1;
  
        for (i = 0; i < needs_pushing.nr; i++) {
                const char *path = needs_pushing.items[i].string;
                fprintf(stderr, "Pushing submodule '%s'\n", path);
 -              if (!push_submodule(path)) {
 +              if (!push_submodule(path, dry_run)) {
                        fprintf(stderr, "Unable to push submodule '%s'\n", path);
                        ret = 0;
                }
@@@ -1297,6 -1272,30 +1347,6 @@@ int merge_submodule(unsigned char resul
        return 0;
  }
  
 -/* 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)
 -{
 -      struct strbuf file_name = STRBUF_INIT;
 -      struct strbuf rel_path = STRBUF_INIT;
 -      const char *real_work_tree = real_pathdup(work_tree);
 -
 -      /* Update gitfile */
 -      strbuf_addf(&file_name, "%s/.git", work_tree);
 -      write_file(file_name.buf, "gitdir: %s",
 -                 relative_path(git_dir, real_work_tree, &rel_path));
 -
 -      /* Update core.worktree setting */
 -      strbuf_reset(&file_name);
 -      strbuf_addf(&file_name, "%s/config", git_dir);
 -      git_config_set_in_file(file_name.buf, "core.worktree",
 -                             relative_path(real_work_tree, git_dir,
 -                                           &rel_path));
 -
 -      strbuf_release(&file_name);
 -      strbuf_release(&rel_path);
 -      free((void *)real_work_tree);
 -}
 -
  int parallel_submodules(void)
  {
        return parallel_jobs;
@@@ -1312,105 -1311,3 +1362,105 @@@ void prepare_submodule_repo_env(struct 
        }
        argv_array_push(out, "GIT_DIR=.git");
  }
-       real_old_git_dir = xstrdup(real_path(old_git_dir));
 +
 +/*
 + * Embeds a single submodules git directory into the superprojects git dir,
 + * non recursively.
 + */
 +static void relocate_single_git_dir_into_superproject(const char *prefix,
 +                                                    const char *path)
 +{
 +      char *old_git_dir = NULL, *real_old_git_dir = NULL, *real_new_git_dir = NULL;
 +      const char *new_git_dir;
 +      const struct submodule *sub;
 +
 +      if (submodule_uses_worktrees(path))
 +              die(_("relocate_gitdir for submodule '%s' with "
 +                    "more than one worktree not supported"), path);
 +
 +      old_git_dir = xstrfmt("%s/.git", path);
 +      if (read_gitfile(old_git_dir))
 +              /* If it is an actual gitfile, it doesn't need migration. */
 +              return;
 +
-       real_new_git_dir = xstrdup(real_path(new_git_dir));
++      real_old_git_dir = real_pathdup(old_git_dir);
 +
 +      sub = submodule_from_path(null_sha1, path);
 +      if (!sub)
 +              die(_("could not lookup name for submodule '%s'"), path);
 +
 +      new_git_dir = git_path("modules/%s", sub->name);
 +      if (safe_create_leading_directories_const(new_git_dir) < 0)
 +              die(_("could not create directory '%s'"), new_git_dir);
-       real_sub_git_dir = xstrdup(real_path(sub_git_dir));
-       real_common_git_dir = xstrdup(real_path(get_git_common_dir()));
++      real_new_git_dir = real_pathdup(new_git_dir);
 +
 +      if (!prefix)
 +              prefix = get_super_prefix();
 +
 +      fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
 +              prefix ? prefix : "", path,
 +              real_old_git_dir, real_new_git_dir);
 +
 +      relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
 +
 +      free(old_git_dir);
 +      free(real_old_git_dir);
 +      free(real_new_git_dir);
 +}
 +
 +/*
 + * Migrate the git directory of the submodule given by path from
 + * having its git directory within the working tree to the git dir nested
 + * in its superprojects git dir under modules/.
 + */
 +void absorb_git_dir_into_superproject(const char *prefix,
 +                                    const char *path,
 +                                    unsigned flags)
 +{
 +      const char *sub_git_dir, *v;
 +      char *real_sub_git_dir = NULL, *real_common_git_dir = NULL;
 +      struct strbuf gitdir = STRBUF_INIT;
 +
 +      strbuf_addf(&gitdir, "%s/.git", path);
 +      sub_git_dir = resolve_gitdir(gitdir.buf);
 +
 +      /* Not populated? */
 +      if (!sub_git_dir)
 +              goto out;
 +
 +      /* Is it already absorbed into the superprojects git dir? */
++      real_sub_git_dir = real_pathdup(sub_git_dir);
++      real_common_git_dir = real_pathdup(get_git_common_dir());
 +      if (!skip_prefix(real_sub_git_dir, real_common_git_dir, &v))
 +              relocate_single_git_dir_into_superproject(prefix, path);
 +
 +      if (flags & ABSORB_GITDIR_RECURSE_SUBMODULES) {
 +              struct child_process cp = CHILD_PROCESS_INIT;
 +              struct strbuf sb = STRBUF_INIT;
 +
 +              if (flags & ~ABSORB_GITDIR_RECURSE_SUBMODULES)
 +                      die("BUG: we don't know how to pass the flags down?");
 +
 +              if (get_super_prefix())
 +                      strbuf_addstr(&sb, get_super_prefix());
 +              strbuf_addstr(&sb, path);
 +              strbuf_addch(&sb, '/');
 +
 +              cp.dir = path;
 +              cp.git_cmd = 1;
 +              cp.no_stdin = 1;
 +              argv_array_pushl(&cp.args, "--super-prefix", sb.buf,
 +                                         "submodule--helper",
 +                                         "absorb-git-dirs", NULL);
 +              prepare_submodule_repo_env(&cp.env_array);
 +              if (run_command(&cp))
 +                      die(_("could not recurse into submodule '%s'"), path);
 +
 +              strbuf_release(&sb);
 +      }
 +
 +out:
 +      strbuf_release(&gitdir);
 +      free(real_sub_git_dir);
 +      free(real_common_git_dir);
 +}
diff --combined submodule.h
index b7576d6f43fee1d7a537bb61cbe02e5780dffa38,9203d89a595a9c959a6a99ea51ac52a52860d23c..1ccaf0e6ba29168d3fe0a2354d4c643e4f3a080e
@@@ -3,7 -3,6 +3,7 @@@
  
  struct diff_options;
  struct argv_array;
 +struct sha1_array;
  
  enum {
        RECURSE_SUBMODULES_CHECK = -4,
@@@ -38,6 -37,9 +38,9 @@@ void set_diffopt_flags_from_submodule_c
                const char *path);
  int submodule_config(const char *var, const char *value, void *cb);
  void gitmodules_config(void);
+ extern void gitmodules_config_sha1(const unsigned char *commit_sha1);
+ extern int is_submodule_initialized(const char *path);
+ extern int is_submodule_populated(const char *path);
  int parse_submodule_update_strategy(const char *value,
                struct submodule_update_strategy *dst);
  const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
@@@ -63,11 -65,10 +66,11 @@@ int submodule_uses_gitfile(const char *
  int ok_to_remove_submodule(const char *path);
  int merge_submodule(unsigned char result[20], const char *path, const unsigned char base[20],
                    const unsigned char a[20], const unsigned char b[20], int search);
 -int find_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name,
 +int find_unpushed_submodules(struct sha1_array *commits, const char *remotes_name,
                struct string_list *needs_pushing);
 -int push_unpushed_submodules(unsigned char new_sha1[20], const char *remotes_name);
 -void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
 +extern int push_unpushed_submodules(struct sha1_array *commits,
 +                                  const char *remotes_name,
 +                                  int dry_run);
  int parallel_submodules(void);
  
  /*
@@@ -77,8 -78,4 +80,8 @@@
   */
  void prepare_submodule_repo_env(struct argv_array *out);
  
 +#define ABSORB_GITDIR_RECURSE_SUBMODULES (1<<0)
 +extern void absorb_git_dir_into_superproject(const char *prefix,
 +                                           const char *path,
 +                                           unsigned flags);
  #endif
diff --combined transport.c
index 3e8799a6111b3c8672e4068450c9d225f3fcccc5,236c6f6b095ad508c57e6ff1048d3b7ca623ca2e..c86ba2eb897f4ee2e45dadd418587ee4cb76401e
@@@ -664,89 -664,21 +664,89 @@@ static const struct string_list *protoc
        return enabled ? &allowed : NULL;
  }
  
 -int is_transport_allowed(const char *type)
 +enum protocol_allow_config {
 +      PROTOCOL_ALLOW_NEVER = 0,
 +      PROTOCOL_ALLOW_USER_ONLY,
 +      PROTOCOL_ALLOW_ALWAYS
 +};
 +
 +static enum protocol_allow_config parse_protocol_config(const char *key,
 +                                                      const char *value)
  {
 -      const struct string_list *allowed = protocol_whitelist();
 -      return !allowed || string_list_has_string(allowed, type);
 +      if (!strcasecmp(value, "always"))
 +              return PROTOCOL_ALLOW_ALWAYS;
 +      else if (!strcasecmp(value, "never"))
 +              return PROTOCOL_ALLOW_NEVER;
 +      else if (!strcasecmp(value, "user"))
 +              return PROTOCOL_ALLOW_USER_ONLY;
 +
 +      die("unknown value for config '%s': %s", key, value);
  }
  
 -void transport_check_allowed(const char *type)
 +static enum protocol_allow_config get_protocol_config(const char *type)
  {
 -      if (!is_transport_allowed(type))
 -              die("transport '%s' not allowed", type);
 +      char *key = xstrfmt("protocol.%s.allow", type);
 +      char *value;
 +
 +      /* first check the per-protocol config */
 +      if (!git_config_get_string(key, &value)) {
 +              enum protocol_allow_config ret =
 +                      parse_protocol_config(key, value);
 +              free(key);
 +              free(value);
 +              return ret;
 +      }
 +      free(key);
 +
 +      /* if defined, fallback to user-defined default for unknown protocols */
 +      if (!git_config_get_string("protocol.allow", &value)) {
 +              enum protocol_allow_config ret =
 +                      parse_protocol_config("protocol.allow", value);
 +              free(value);
 +              return ret;
 +      }
 +
 +      /* fallback to built-in defaults */
 +      /* known safe */
 +      if (!strcmp(type, "http") ||
 +          !strcmp(type, "https") ||
 +          !strcmp(type, "git") ||
 +          !strcmp(type, "ssh") ||
 +          !strcmp(type, "file"))
 +              return PROTOCOL_ALLOW_ALWAYS;
 +
 +      /* known scary; err on the side of caution */
 +      if (!strcmp(type, "ext"))
 +              return PROTOCOL_ALLOW_NEVER;
 +
 +      /* unknown; by default let them be used only directly by the user */
 +      return PROTOCOL_ALLOW_USER_ONLY;
 +}
 +
 +int is_transport_allowed(const char *type, int from_user)
 +{
 +      const struct string_list *whitelist = protocol_whitelist();
 +      if (whitelist)
 +              return string_list_has_string(whitelist, type);
 +
 +      switch (get_protocol_config(type)) {
 +      case PROTOCOL_ALLOW_ALWAYS:
 +              return 1;
 +      case PROTOCOL_ALLOW_NEVER:
 +              return 0;
 +      case PROTOCOL_ALLOW_USER_ONLY:
 +              if (from_user < 0)
 +                      from_user = git_env_bool("GIT_PROTOCOL_FROM_USER", 1);
 +              return from_user;
 +      }
 +
 +      die("BUG: invalid protocol_allow_config type");
  }
  
 -int transport_restrict_protocols(void)
 +void transport_check_allowed(const char *type)
  {
 -      return !!protocol_whitelist();
 +      if (!is_transport_allowed(type, -1))
 +              die("transport '%s' not allowed", type);
  }
  
  struct transport *transport_get(struct remote *remote, const char *url)
@@@ -1017,39 -949,23 +1017,39 @@@ int transport_push(struct transport *tr
  
                if ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
 +                      struct sha1_array commits = SHA1_ARRAY_INIT;
 +
                        for (; ref; ref = ref->next)
 -                              if (!is_null_oid(&ref->new_oid) &&
 -                                  !push_unpushed_submodules(ref->new_oid.hash,
 -                                          transport->remote->name))
 -                                  die ("Failed to push all needed submodules!");
 +                              if (!is_null_oid(&ref->new_oid))
 +                                      sha1_array_append(&commits, ref->new_oid.hash);
 +
 +                      if (!push_unpushed_submodules(&commits,
 +                                                    transport->remote->name,
 +                                                    pretend)) {
 +                              sha1_array_clear(&commits);
 +                              die("Failed to push all needed submodules!");
 +                      }
 +                      sha1_array_clear(&commits);
                }
  
 -              if ((flags & (TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND |
 -                            TRANSPORT_RECURSE_SUBMODULES_CHECK)) && !is_bare_repository()) {
 +              if (((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) ||
 +                   ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) &&
 +                    !pretend)) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
                        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
 +                      struct sha1_array commits = SHA1_ARRAY_INIT;
  
                        for (; ref; ref = ref->next)
 -                              if (!is_null_oid(&ref->new_oid) &&
 -                                  find_unpushed_submodules(ref->new_oid.hash,
 -                                          transport->remote->name, &needs_pushing))
 -                                      die_with_unpushed_submodules(&needs_pushing);
 +                              if (!is_null_oid(&ref->new_oid))
 +                                      sha1_array_append(&commits, ref->new_oid.hash);
 +
 +                      if (find_unpushed_submodules(&commits, transport->remote->name,
 +                                              &needs_pushing)) {
 +                              sha1_array_clear(&commits);
 +                              die_with_unpushed_submodules(&needs_pushing);
 +                      }
 +                      string_list_clear(&needs_pushing, 0);
 +                      sha1_array_clear(&commits);
                }
  
                push_ret = transport->push_refs(transport, remote_refs, flags);
@@@ -1214,7 -1130,7 +1214,7 @@@ static int refs_from_alternate_cb(struc
        const struct ref *extra;
        struct alternate_refs_data *cb = data;
  
-       other = xstrdup(real_path(e->path));
+       other = real_pathdup(e->path);
        len = strlen(other);
  
        while (other[len-1] == '/')
diff --combined worktree.c
index 828fd7a0ad4243749acffb9ef7885e95b8bb2bd0,c90e0139855bd39343f262c557fd57daaa14650b..53b4771c04a2b9de15379fdd213f20f213b1be60
@@@ -88,13 -88,21 +88,13 @@@ static struct worktree *get_main_worktr
  
        strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
  
 -      if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
 -              goto done;
 -
 -      worktree = xmalloc(sizeof(struct worktree));
 +      worktree = xcalloc(1, sizeof(*worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
 -      worktree->id = NULL;
        worktree->is_bare = is_bare;
 -      worktree->head_ref = NULL;
        worktree->is_detached = is_detached;
 -      worktree->is_current = 0;
 -      add_head_info(&head_ref, worktree);
 -      worktree->lock_reason = NULL;
 -      worktree->lock_reason_valid = 0;
 +      if (!parse_ref(path.buf, &head_ref, &is_detached))
 +              add_head_info(&head_ref, worktree);
  
 -done:
        strbuf_release(&path);
        strbuf_release(&worktree_path);
        strbuf_release(&head_ref);
@@@ -130,11 -138,16 +130,11 @@@ static struct worktree *get_linked_work
        if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
                goto done;
  
 -      worktree = xmalloc(sizeof(struct worktree));
 +      worktree = xcalloc(1, sizeof(*worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
        worktree->id = xstrdup(id);
 -      worktree->is_bare = 0;
 -      worktree->head_ref = NULL;
        worktree->is_detached = is_detached;
 -      worktree->is_current = 0;
        add_head_info(&head_ref, worktree);
 -      worktree->lock_reason = NULL;
 -      worktree->lock_reason_valid = 0;
  
  done:
        strbuf_release(&path);
@@@ -160,14 -173,7 +160,14 @@@ static void mark_current_worktree(struc
        free(git_dir);
  }
  
 -struct worktree **get_worktrees(void)
 +static int compare_worktree(const void *a_, const void *b_)
 +{
 +      const struct worktree *const *a = a_;
 +      const struct worktree *const *b = b_;
 +      return fspathcmp((*a)->path, (*b)->path);
 +}
 +
 +struct worktree **get_worktrees(unsigned flags)
  {
        struct worktree **list = NULL;
        struct strbuf path = STRBUF_INIT;
  
        list = xmalloc(alloc * sizeof(struct worktree *));
  
 -      if ((list[counter] = get_main_worktree()))
 -              counter++;
 +      list[counter++] = get_main_worktree();
  
        strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
        dir = opendir(path.buf);
        ALLOC_GROW(list, counter + 1, alloc);
        list[counter] = NULL;
  
 +      if (flags & GWT_SORT_LINKED)
 +              /*
 +               * don't sort the first item (main worktree), which will
 +               * always be the first
 +               */
 +              QSORT(list + 1, counter - 1, compare_worktree);
 +
        mark_current_worktree(list);
        return list;
  }
@@@ -255,7 -255,7 +255,7 @@@ struct worktree *find_worktree(struct w
                return wt;
  
        arg = prefix_filename(prefix, strlen(prefix), arg);
-       path = xstrdup(real_path(arg));
+       path = real_pathdup(arg);
        for (; *list; list++)
                if (!fspathcmp(path, real_path((*list)->path)))
                        break;
@@@ -341,7 -341,7 +341,7 @@@ const struct worktree *find_shared_symr
  
        if (worktrees)
                free_worktrees(worktrees);
 -      worktrees = get_worktrees();
 +      worktrees = get_worktrees(0);
  
        for (i = 0; worktrees[i]; i++) {
                struct worktree *wt = worktrees[i];
  
        return existing;
  }
 +
 +int submodule_uses_worktrees(const char *path)
 +{
 +      char *submodule_gitdir;
 +      struct strbuf sb = STRBUF_INIT;
 +      DIR *dir;
 +      struct dirent *d;
 +      int ret = 0;
 +      struct repository_format format;
 +
 +      submodule_gitdir = git_pathdup_submodule(path, "%s", "");
 +      if (!submodule_gitdir)
 +              return 0;
 +
 +      /* The env would be set for the superproject. */
 +      get_common_dir_noenv(&sb, submodule_gitdir);
 +
 +      /*
 +       * The check below is only known to be good for repository format
 +       * version 0 at the time of writing this code.
 +       */
 +      strbuf_addstr(&sb, "/config");
 +      read_repository_format(&format, sb.buf);
 +      if (format.version != 0) {
 +              strbuf_release(&sb);
 +              return 1;
 +      }
 +
 +      /* Replace config by worktrees. */
 +      strbuf_setlen(&sb, sb.len - strlen("config"));
 +      strbuf_addstr(&sb, "worktrees");
 +
 +      /* See if there is any file inside the worktrees directory. */
 +      dir = opendir(sb.buf);
 +      strbuf_release(&sb);
 +      free(submodule_gitdir);
 +
 +      if (!dir)
 +              return 0;
 +
 +      while ((d = readdir(dir)) != NULL) {
 +              if (is_dot_or_dotdot(d->d_name))
 +                      continue;
 +
 +              ret = 1;
 +              break;
 +      }
 +      closedir(dir);
 +      return ret;
 +}