Merge branch 'sb/pull-rebase-submodule'
authorJunio C Hamano <gitster@pobox.com>
Thu, 13 Jul 2017 23:14:54 +0000 (16:14 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 13 Jul 2017 23:14:54 +0000 (16:14 -0700)
"git pull --rebase --recurse-submodules" learns to rebase the
branch in the submodules to an updated base.

* sb/pull-rebase-submodule:
builtin/fetch cleanup: always set default value for submodule recursing
pull: optionally rebase submodules (remote submodule changes only)
builtin/fetch: parse recurse-submodules-default at default options parsing
builtin/fetch: factor submodule recurse parsing out to submodule config

1  2 
Documentation/git-pull.txt
builtin/fetch.c
builtin/pull.c
submodule-config.c
submodule-config.h
submodule.c
submodule.h
index 9db5e08f4a63ae6b15b37ecf7ce96ee728716195,b201af6f19b5d137e010dd9a25d746c7d9031007..ce05b7a5b13eadb6870f16a7168559434d376c20
@@@ -86,12 -86,12 +86,12 @@@ OPTION
  
  --[no-]recurse-submodules[=yes|on-demand|no]::
        This option controls if new commits of all populated submodules should
-       be fetched too (see linkgit:git-config[1] and linkgit:gitmodules[5]).
-       That might be necessary to get the data needed for merging submodule
-       commits, a feature Git learned in 1.7.3. Notice that the result of a
-       merge will not be checked out in the submodule, "git submodule update"
-       has to be called afterwards to bring the work tree up to date with the
      merge result.
+       be fetched and updated, too (see linkgit:git-config[1] and
+       linkgit:gitmodules[5]).
+ +
+ If the checkout is done via rebase, local submodule commits are rebased as well.
+ +
If the update is done via merge, the submodule conflicts are resolved and checked out.
  
  Options related to merging
  ~~~~~~~~~~~~~~~~~~~~~~~~~~
@@@ -131,7 -131,7 +131,7 @@@ unless you have read linkgit:git-rebase
  --autostash::
  --no-autostash::
        Before starting rebase, stash local modifications away (see
 -      linkgit:git-stash[1]) if needed, and apply the stash when
 +      linkgit:git-stash[1]) if needed, and apply the stash entry when
        done. `--no-autostash` is useful to override the `rebase.autoStash`
        configuration variable (see linkgit:git-config[1]).
  +
diff --combined builtin/fetch.c
index 1838a9abfba61b76a52e96520b9dc559cf4747a2,7ea52b8b073ab7292e3be9360f80b1e91cdcb46c..c87e59f3b1def1f064e0dae54a14e310c06df1fa
@@@ -2,7 -2,6 +2,7 @@@
   * "git fetch"
   */
  #include "cache.h"
 +#include "config.h"
  #include "refs.h"
  #include "commit.h"
  #include "builtin.h"
@@@ -37,7 -36,7 +37,7 @@@ static int prune = -1; /* unspecified *
  #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
  
  static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
- static int progress = -1, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+ static int progress = -1;
  static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
  static int max_children = -1;
  static enum transport_family family;
@@@ -49,25 -48,12 +49,12 @@@ static struct strbuf default_rla = STRB
  static struct transport *gtransport;
  static struct transport *gsecondary;
  static const char *submodule_prefix = "";
- static const char *recurse_submodules_default;
+ static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+ static int recurse_submodules_default = RECURSE_SUBMODULES_ON_DEMAND;
  static int shown_url = 0;
  static int refmap_alloc, refmap_nr;
  static const char **refmap_array;
  
- static int option_parse_recurse_submodules(const struct option *opt,
-                                  const char *arg, int unset)
- {
-       if (unset) {
-               recurse_submodules = RECURSE_SUBMODULES_OFF;
-       } else {
-               if (arg)
-                       recurse_submodules = parse_fetch_recurse_submodules_arg(opt->long_name, arg);
-               else
-                       recurse_submodules = RECURSE_SUBMODULES_ON;
-       }
-       return 0;
- }
  static int git_fetch_config(const char *k, const char *v, void *cb)
  {
        if (!strcmp(k, "fetch.prune")) {
@@@ -116,9 -102,9 +103,9 @@@ static struct option builtin_fetch_opti
                    N_("number of submodules fetched in parallel")),
        OPT_BOOL('p', "prune", &prune,
                 N_("prune remote-tracking branches no longer on remote")),
-       { OPTION_CALLBACK, 0, "recurse-submodules", NULL, N_("on-demand"),
+       { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, N_("on-demand"),
                    N_("control recursive fetching of submodules"),
-                   PARSE_OPT_OPTARG, option_parse_recurse_submodules },
+                   PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
        OPT_BOOL(0, "dry-run", &dry_run,
                 N_("dry run")),
        OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
                   PARSE_OPT_NONEG | PARSE_OPT_NOARG, NULL, 1 },
        { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, N_("dir"),
                   N_("prepend this to submodule path output"), PARSE_OPT_HIDDEN },
-       { OPTION_STRING, 0, "recurse-submodules-default",
-                  &recurse_submodules_default, NULL,
-                  N_("default mode for recursion"), PARSE_OPT_HIDDEN },
+       { OPTION_CALLBACK, 0, "recurse-submodules-default",
+                  &recurse_submodules_default, N_("on-demand"),
+                  N_("default for recursive fetching of submodules "
+                     "(lower priority than config files)"),
+                  PARSE_OPT_HIDDEN, option_fetch_parse_recurse_submodules },
        OPT_BOOL(0, "update-shallow", &update_shallow,
                 N_("accept refs that update .git/shallow")),
        { OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
@@@ -250,11 -238,9 +239,11 @@@ static void find_non_local_tags(struct 
                 */
                if (ends_with(ref->name, "^{}")) {
                        if (item &&
 -                          !has_object_file_with_flags(&ref->old_oid, HAS_SHA1_QUICK) &&
 +                          !has_object_file_with_flags(&ref->old_oid,
 +                                                      OBJECT_INFO_QUICK) &&
                            !will_fetch(head, ref->old_oid.hash) &&
 -                          !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
 +                          !has_sha1_file_with_flags(item->util,
 +                                                    OBJECT_INFO_QUICK) &&
                            !will_fetch(head, item->util))
                                item->util = NULL;
                        item = NULL;
                 * fetch.
                 */
                if (item &&
 -                  !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
 +                  !has_sha1_file_with_flags(item->util, OBJECT_INFO_QUICK) &&
                    !will_fetch(head, item->util))
                        item->util = NULL;
  
         * checked to see if it needs fetching.
         */
        if (item &&
 -          !has_sha1_file_with_flags(item->util, HAS_SHA1_QUICK) &&
 +          !has_sha1_file_with_flags(item->util, OBJECT_INFO_QUICK) &&
            !will_fetch(head, item->util))
                item->util = NULL;
  
@@@ -1350,10 -1336,7 +1339,7 @@@ int cmd_fetch(int argc, const char **ar
                deepen = 1;
  
        if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
-               if (recurse_submodules_default) {
-                       int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
-                       set_config_fetch_recurse_submodules(arg);
-               }
+               set_config_fetch_recurse_submodules(recurse_submodules_default);
                gitmodules_config();
                git_config(submodule_config, NULL);
        }
diff --combined builtin/pull.c
index 2ce311a52eb6111fd16951c860d194dc35300438,7048fdf0057383e84a73f8cf55374b13beda0b2e..9b86e519b19a6180e52fda3342dec4f080e1f9e9
@@@ -6,7 -6,6 +6,7 @@@
   * Fetch one or more remote refs and merge it/them into the current HEAD.
   */
  #include "cache.h"
 +#include "config.h"
  #include "builtin.h"
  #include "parse-options.h"
  #include "exec_cmd.h"
@@@ -16,6 -15,8 +16,8 @@@
  #include "dir.h"
  #include "refs.h"
  #include "revision.h"
+ #include "submodule.h"
+ #include "submodule-config.h"
  #include "tempfile.h"
  #include "lockfile.h"
  #include "wt-status.h"
@@@ -78,6 -79,7 +80,7 @@@ static const char * const pull_usage[] 
  /* Shared options */
  static int opt_verbosity;
  static char *opt_progress;
+ static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
  
  /* Options passed to git-merge or git-rebase */
  static enum rebase_type opt_rebase = -1;
@@@ -102,7 -104,6 +105,6 @@@ static char *opt_upload_pack
  static int opt_force;
  static char *opt_tags;
  static char *opt_prune;
- static char *opt_recurse_submodules;
  static char *max_children;
  static int opt_dry_run;
  static char *opt_keep;
@@@ -117,6 -118,10 +119,10 @@@ static struct option pull_options[] = 
        OPT_PASSTHRU(0, "progress", &opt_progress, NULL,
                N_("force progress reporting"),
                PARSE_OPT_NOARG),
+       { OPTION_CALLBACK, 0, "recurse-submodules",
+                  &recurse_submodules, N_("on-demand"),
+                  N_("control for recursive fetching of submodules"),
+                  PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
  
        /* Options passed to git-merge or git-rebase */
        OPT_GROUP(N_("Options related to merging")),
        OPT_PASSTHRU('p', "prune", &opt_prune, NULL,
                N_("prune remote-tracking branches no longer on remote"),
                PARSE_OPT_NOARG),
-       OPT_PASSTHRU(0, "recurse-submodules", &opt_recurse_submodules,
-               N_("on-demand"),
-               N_("control recursive fetching of submodules"),
-               PARSE_OPT_OPTARG),
        OPT_PASSTHRU('j', "jobs", &max_children, N_("n"),
                N_("number of submodules pulled in parallel"),
                PARSE_OPT_OPTARG),
@@@ -484,8 -485,20 +486,20 @@@ static int run_fetch(const char *repo, 
                argv_array_push(&args, opt_tags);
        if (opt_prune)
                argv_array_push(&args, opt_prune);
-       if (opt_recurse_submodules)
-               argv_array_push(&args, opt_recurse_submodules);
+       if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
+               switch (recurse_submodules) {
+               case RECURSE_SUBMODULES_ON:
+                       argv_array_push(&args, "--recurse-submodules=on");
+                       break;
+               case RECURSE_SUBMODULES_OFF:
+                       argv_array_push(&args, "--recurse-submodules=no");
+                       break;
+               case RECURSE_SUBMODULES_ON_DEMAND:
+                       argv_array_push(&args, "--recurse-submodules=on-demand");
+                       break;
+               default:
+                       BUG("submodule recursion option not understood");
+               }
        if (max_children)
                argv_array_push(&args, max_children);
        if (opt_dry_run)
@@@ -532,6 -545,30 +546,30 @@@ static int pull_into_void(const struct 
        return 0;
  }
  
+ static int rebase_submodules(void)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       argv_array_pushl(&cp.args, "submodule", "update",
+                                  "--recursive", "--rebase", NULL);
+       return run_command(&cp);
+ }
+ static int update_submodules(void)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       argv_array_pushl(&cp.args, "submodule", "update",
+                                  "--recursive", "--checkout", NULL);
+       return run_command(&cp);
+ }
  /**
   * Runs git-merge, returning its exit status.
   */
@@@ -863,6 -900,11 +901,11 @@@ int cmd_pull(int argc, const char **arg
                die(_("Cannot rebase onto multiple branches."));
  
        if (opt_rebase) {
+               int ret = 0;
+               if ((recurse_submodules == RECURSE_SUBMODULES_ON ||
+                    recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) &&
+                   submodule_touches_in_range(&rebase_fork_point, &curr_head))
+                       die(_("cannot rebase with locally recorded submodule modifications"));
                if (!autostash) {
                        struct commit_list *list = NULL;
                        struct commit *merge_head, *head;
                        if (is_descendant_of(merge_head, list)) {
                                /* we can fast-forward this without invoking rebase */
                                opt_ff = "--ff-only";
-                               return run_merge();
+                               ret = run_merge();
                        }
                }
-               return run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
+               ret = run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
+               if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
+                            recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
+                       ret = rebase_submodules();
+               return ret;
        } else {
-               return run_merge();
+               int ret = run_merge();
+               if (!ret && (recurse_submodules == RECURSE_SUBMODULES_ON ||
+                            recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND))
+                       ret = update_submodules();
+               return ret;
        }
  }
diff --combined submodule-config.c
index eeb154a894d6bfdec877d94d5922438b5f934730,265d03609528b12fa531d1ee455bea7a54686428..5fe2d0787745de4d97218c738b808667dc8348d3
@@@ -1,9 -1,8 +1,10 @@@
  #include "cache.h"
 +#include "repository.h"
 +#include "config.h"
  #include "submodule-config.h"
  #include "submodule.h"
  #include "strbuf.h"
+ #include "parse-options.h"
  
  /*
   * submodule cache lookup structure
@@@ -16,7 -15,6 +17,7 @@@
  struct submodule_cache {
        struct hashmap for_path;
        struct hashmap for_name;
 +      unsigned initialized:1;
  };
  
  /*
@@@ -33,34 -31,29 +34,34 @@@ enum lookup_type 
        lookup_path
  };
  
 -static struct submodule_cache the_submodule_cache;
 -static int is_cache_init;
 -
 -static int config_path_cmp(const struct submodule_entry *a,
 +static int config_path_cmp(const void *unused_cmp_data,
 +                         const struct submodule_entry *a,
                           const struct submodule_entry *b,
 -                         const void *unused)
 +                         const void *unused_keydata)
  {
        return strcmp(a->config->path, b->config->path) ||
               hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
  }
  
 -static int config_name_cmp(const struct submodule_entry *a,
 +static int config_name_cmp(const void *unused_cmp_data,
 +                         const struct submodule_entry *a,
                           const struct submodule_entry *b,
 -                         const void *unused)
 +                         const void *unused_keydata)
  {
        return strcmp(a->config->name, b->config->name) ||
               hashcmp(a->config->gitmodules_sha1, b->config->gitmodules_sha1);
  }
  
 -static void cache_init(struct submodule_cache *cache)
 +static struct submodule_cache *submodule_cache_alloc(void)
  {
 -      hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, 0);
 -      hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, 0);
 +      return xcalloc(1, sizeof(struct submodule_cache));
 +}
 +
 +static void submodule_cache_init(struct submodule_cache *cache)
 +{
 +      hashmap_init(&cache->for_path, (hashmap_cmp_fn) config_path_cmp, NULL, 0);
 +      hashmap_init(&cache->for_name, (hashmap_cmp_fn) config_name_cmp, NULL, 0);
 +      cache->initialized = 1;
  }
  
  static void free_one_config(struct submodule_entry *entry)
        free(entry->config);
  }
  
 -static void cache_free(struct submodule_cache *cache)
 +static void submodule_cache_clear(struct submodule_cache *cache)
  {
        struct hashmap_iter iter;
        struct submodule_entry *entry;
  
 +      if (!cache->initialized)
 +              return;
 +
        /*
         * We iterate over the name hash here to be symmetric with the
         * allocation of struct submodule entries. Each is allocated by
  
        hashmap_free(&cache->for_path, 1);
        hashmap_free(&cache->for_name, 1);
 +      cache->initialized = 0;
 +}
 +
 +void submodule_cache_free(struct submodule_cache *cache)
 +{
 +      submodule_cache_clear(cache);
 +      free(cache);
  }
  
  static unsigned int hash_sha1_string(const unsigned char *sha1,
@@@ -252,6 -235,27 +253,27 @@@ int parse_fetch_recurse_submodules_arg(
        return parse_fetch_recurse(opt, arg, 1);
  }
  
+ int option_fetch_parse_recurse_submodules(const struct option *opt,
+                                         const char *arg, int unset)
+ {
+       int *v;
+       if (!opt->value)
+               return -1;
+       v = opt->value;
+       if (unset) {
+               *v = RECURSE_SUBMODULES_OFF;
+       } else {
+               if (arg)
+                       *v = parse_fetch_recurse_submodules_arg(opt->long_name, arg);
+               else
+                       *v = RECURSE_SUBMODULES_ON;
+       }
+       return 0;
+ }
  static int parse_update_recurse(const char *opt, const char *arg,
                                int die_on_error)
  {
@@@ -511,62 -515,43 +533,62 @@@ out
        return submodule;
  }
  
 -static void ensure_cache_init(void)
 +static void submodule_cache_check_init(struct repository *repo)
  {
 -      if (is_cache_init)
 +      if (repo->submodule_cache && repo->submodule_cache->initialized)
                return;
  
 -      cache_init(&the_submodule_cache);
 -      is_cache_init = 1;
 +      if (!repo->submodule_cache)
 +              repo->submodule_cache = submodule_cache_alloc();
 +
 +      submodule_cache_init(repo->submodule_cache);
  }
  
 -int parse_submodule_config_option(const char *var, const char *value)
 +int submodule_config_option(struct repository *repo,
 +                          const char *var, const char *value)
  {
        struct parse_config_parameter parameter;
 -      parameter.cache = &the_submodule_cache;
 +
 +      submodule_cache_check_init(repo);
 +
 +      parameter.cache = repo->submodule_cache;
        parameter.treeish_name = NULL;
        parameter.gitmodules_sha1 = null_sha1;
        parameter.overwrite = 1;
  
 -      ensure_cache_init();
        return parse_config(var, value, &parameter);
  }
  
 +int parse_submodule_config_option(const char *var, const char *value)
 +{
 +      return submodule_config_option(the_repository, var, value);
 +}
 +
  const struct submodule *submodule_from_name(const unsigned char *treeish_name,
                const char *name)
  {
 -      ensure_cache_init();
 -      return config_from(&the_submodule_cache, treeish_name, name, lookup_name);
 +      submodule_cache_check_init(the_repository);
 +      return config_from(the_repository->submodule_cache, treeish_name, name, lookup_name);
  }
  
  const struct submodule *submodule_from_path(const unsigned char *treeish_name,
                const char *path)
  {
 -      ensure_cache_init();
 -      return config_from(&the_submodule_cache, treeish_name, path, lookup_path);
 +      submodule_cache_check_init(the_repository);
 +      return config_from(the_repository->submodule_cache, treeish_name, path, lookup_path);
 +}
 +
 +const struct submodule *submodule_from_cache(struct repository *repo,
 +                                           const unsigned char *treeish_name,
 +                                           const char *key)
 +{
 +      submodule_cache_check_init(repo);
 +      return config_from(repo->submodule_cache, treeish_name,
 +                         key, lookup_path);
  }
  
  void submodule_free(void)
  {
 -      cache_free(&the_submodule_cache);
 -      is_cache_init = 0;
 +      if (the_repository->submodule_cache)
 +              submodule_cache_clear(the_repository->submodule_cache);
  }
diff --combined submodule-config.h
index bc45a25e850eb600eb9883d83d1ec1d3fc08db4a,1076a68653c13f2cd4475d0fa0b3f55ca2b7fab7..233bfcb7fff0b2dedc64fbb11d167734bf0fad7e
@@@ -22,24 -22,17 +22,27 @@@ struct submodule 
        int recommend_shallow;
  };
  
 +struct submodule_cache;
 +struct repository;
 +
 +extern void submodule_cache_free(struct submodule_cache *cache);
 +
  extern int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
+ struct option;
+ extern int option_fetch_parse_recurse_submodules(const struct option *opt,
+                                                const char *arg, int unset);
  extern int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
  extern int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
  extern int parse_submodule_config_option(const char *var, const char *value);
 +extern int submodule_config_option(struct repository *repo,
 +                                 const char *var, const char *value);
  extern const struct submodule *submodule_from_name(
                const unsigned char *commit_or_tree, const char *name);
  extern const struct submodule *submodule_from_path(
                const unsigned char *commit_or_tree, const char *path);
 +extern const struct submodule *submodule_from_cache(struct repository *repo,
 +                                                  const unsigned char *treeish_name,
 +                                                  const char *key);
  extern int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
                                      unsigned char *gitmodules_sha1,
                                      struct strbuf *rev);
diff --combined submodule.c
index da2b4848791382b30147cd8fe921996cfb8a92a7,6e2e35a7fb2a915f3f68da000d5c3c6b75f78c53..6531c5d6094e47b251e69ce741699abfee7a64df
@@@ -1,6 -1,4 +1,6 @@@
  #include "cache.h"
 +#include "repository.h"
 +#include "config.h"
  #include "submodule-config.h"
  #include "submodule.h"
  #include "dir.h"
@@@ -256,20 -254,6 +256,20 @@@ void gitmodules_config(void
        }
  }
  
 +static int gitmodules_cb(const char *var, const char *value, void *data)
 +{
 +      struct repository *repo = data;
 +      return submodule_config_option(repo, var, value);
 +}
 +
 +void repo_read_gitmodules(struct repository *repo)
 +{
 +      char *gitmodules_path = repo_worktree_path(repo, ".gitmodules");
 +
 +      git_config_from_file(gitmodules_cb, gitmodules_path, repo);
 +      free(gitmodules_path);
 +}
 +
  void gitmodules_config_sha1(const unsigned char *commit_sha1)
  {
        struct strbuf rev = STRBUF_INIT;
  }
  
  /*
 - * NEEDSWORK: With the addition of different configuration options to determine
 - * if a submodule is of interests, the validity of this function's name comes
 - * into question.  Once the dust has settled and more concrete terminology is
 - * decided upon, come up with a more proper name for this function.  One
 - * potential candidate could be 'is_submodule_active()'.
 - *
   * Determine if a submodule has been initialized at a given 'path'
   */
 -int is_submodule_initialized(const char *path)
 +int is_submodule_active(struct repository *repo, const char *path)
  {
        int ret = 0;
        char *key = NULL;
        char *value = NULL;
        const struct string_list *sl;
 -      const struct submodule *module = submodule_from_path(null_sha1, path);
 +      const struct submodule *module;
 +
 +      module = submodule_from_cache(repo, null_sha1, path);
  
        /* early return if there isn't a path->module mapping */
        if (!module)
  
        /* submodule.<name>.active is set */
        key = xstrfmt("submodule.%s.active", module->name);
 -      if (!git_config_get_bool(key, &ret)) {
 +      if (!repo_config_get_bool(repo, key, &ret)) {
                free(key);
                return ret;
        }
        free(key);
  
        /* submodule.active is set */
 -      sl = git_config_get_value_multi("submodule.active");
 +      sl = repo_config_get_value_multi(repo, "submodule.active");
        if (sl) {
                struct pathspec ps;
                struct argv_array args = ARGV_ARRAY_INIT;
  
        /* fallback to checking if the URL is set */
        key = xstrfmt("submodule.%s.url", module->name);
 -      ret = !git_config_get_string(key, &value);
 +      ret = !repo_config_get_string(repo, key, &value);
  
        free(value);
        free(key);
@@@ -857,9 -845,9 +857,9 @@@ static int submodule_has_commits(const 
        int has_commit = 1;
  
        /*
 -       * Perform a cheap, but incorrect check for the existance of 'commits'.
 +       * Perform a cheap, but incorrect check for the existence of 'commits'.
         * This is done by adding the submodule's object store to the in-core
 -       * object store, and then querying for each commit's existance.  If we
 +       * object store, and then querying for each commit's existence.  If we
         * do not have the commit object anywhere, there is no chance we have
         * it in the object store of the correct submodule and have it
         * reachable from a ref, so we can fail early without spawning rev-list
@@@ -1138,6 -1126,32 +1138,32 @@@ static void calculate_changed_submodule
        initialized_fetch_ref_tips = 0;
  }
  
+ int submodule_touches_in_range(struct object_id *excl_oid,
+                              struct object_id *incl_oid)
+ {
+       struct string_list subs = STRING_LIST_INIT_DUP;
+       struct argv_array args = ARGV_ARRAY_INIT;
+       int ret;
+       gitmodules_config();
+       /* No need to check if there are no submodules configured */
+       if (!submodule_from_path(NULL, NULL))
+               return 0;
+       argv_array_push(&args, "--"); /* args[0] program name */
+       argv_array_push(&args, oid_to_hex(incl_oid));
+       argv_array_push(&args, "--not");
+       argv_array_push(&args, oid_to_hex(excl_oid));
+       collect_changed_submodules(&subs, &args);
+       ret = subs.nr;
+       argv_array_clear(&args);
+       free_submodules_oids(&subs);
+       return ret;
+ }
  struct submodule_parallel_fetch {
        int count;
        struct argv_array args;
@@@ -1528,7 -1542,7 +1554,7 @@@ int submodule_move_head(const char *pat
        const struct submodule *sub;
        int *error_code_ptr, error_code;
  
 -      if (!is_submodule_initialized(path))
 +      if (!is_submodule_active(the_repository, path))
                return 0;
  
        if (flags & SUBMODULE_MOVE_HEAD_FORCE)
diff --combined submodule.h
index 623ce6ad7716aa037e2970f6847cc145c41bff69,ab1f01b3cebb34805327f0e27372f53e4241c124..e85b1448638ac7255a48a1bc1e2d6409c3a85ba6
@@@ -1,7 -1,6 +1,7 @@@
  #ifndef SUBMODULE_H
  #define SUBMODULE_H
  
 +struct repository;
  struct diff_options;
  struct argv_array;
  struct oid_array;
@@@ -47,9 -46,8 +47,9 @@@ int option_parse_recurse_submodules_wor
                                                     const char *arg, int unset);
  void load_submodule_cache(void);
  extern void gitmodules_config(void);
 +extern void repo_read_gitmodules(struct repository *repo);
  extern void gitmodules_config_sha1(const unsigned char *commit_sha1);
 -extern int is_submodule_initialized(const char *path);
 +extern int is_submodule_active(struct repository *repo, const char *path);
  /*
   * Determine if a submodule has been populated at a given 'path' by checking if
   * the <path>/.git resolves to a valid git repository.
@@@ -99,6 -97,10 +99,10 @@@ extern int merge_submodule(struct objec
                           const struct object_id *base,
                           const struct object_id *a,
                           const struct object_id *b, int search);
+ /* Checks if there are submodule changes in a..b. */
+ extern int submodule_touches_in_range(struct object_id *a,
+                                     struct object_id *b);
  extern int find_unpushed_submodules(struct oid_array *commits,
                                    const char *remotes_name,
                                    struct string_list *needs_pushing);