Merge branch 'sb/checkout-recurse-submodules'
authorJunio C Hamano <gitster@pobox.com>
Tue, 28 Mar 2017 21:05:58 +0000 (14:05 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 28 Mar 2017 21:05:58 +0000 (14:05 -0700)
"git checkout" is taught the "--recurse-submodules" option.

* sb/checkout-recurse-submodules:
builtin/read-tree: add --recurse-submodules switch
builtin/checkout: add --recurse-submodules switch
entry.c: create submodules when interesting
unpack-trees: check if we can perform the operation for submodules
unpack-trees: pass old oid to verify_clean_submodule
update submodules: add submodule_move_head
submodule.c: get_super_prefix_or_empty
update submodules: move up prepare_submodule_repo_env
submodules: introduce check to see whether to touch a submodule
update submodules: add a config option to determine if submodules are updated
update submodules: add submodule config parsing
make is_submodule_populated gently
lib-submodule-update.sh: define tests for recursing into submodules
lib-submodule-update.sh: replace sha1 by hash
lib-submodule-update: teach test_submodule_content the -C <dir> flag
lib-submodule-update.sh: do not use ./. as submodule remote
lib-submodule-update.sh: reorder create_lib_submodule_repo
submodule--helper.c: remove duplicate code
connect_work_tree_and_git_dir: safely create leading directories

1  2 
builtin/checkout.c
builtin/grep.c
builtin/submodule--helper.c
dir.c
submodule-config.c
submodule.c
submodule.h
diff --combined builtin/checkout.c
index 81f07c3ef271db08e36ee7a89b1d128d099ecb96,e9c5fcfaf85533bee3086511c08de93ea63021ed..3ecc47bec07b47f3452b4744f0e7482681e61748
  #include "submodule-config.h"
  #include "submodule.h"
  
+ static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
  static const char * const checkout_usage[] = {
        N_("git checkout [<options>] <branch>"),
        N_("git checkout [<options>] [<branch>] -- <file>..."),
        NULL,
  };
  
+ static int option_parse_recurse_submodules(const struct option *opt,
+                                          const char *arg, int unset)
+ {
+       if (unset) {
+               recurse_submodules = RECURSE_SUBMODULES_OFF;
+               return 0;
+       }
+       if (arg)
+               recurse_submodules =
+                       parse_update_recurse_submodules_arg(opt->long_name,
+                                                           arg);
+       else
+               recurse_submodules = RECURSE_SUBMODULES_ON;
+       return 0;
+ }
  struct checkout_opts {
        int patch_mode;
        int quiet;
@@@ -452,7 -471,7 +471,7 @@@ static void setup_branch_path(struct br
  {
        struct strbuf buf = STRBUF_INIT;
  
 -      strbuf_branchname(&buf, branch->name);
 +      strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
        if (strcmp(buf.buf, branch->name))
                branch->name = xstrdup(buf.buf);
        strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
@@@ -1163,6 -1182,9 +1182,9 @@@ int cmd_checkout(int argc, const char *
                                N_("second guess 'git checkout <no-such-branch>'")),
                OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
                         N_("do not check if another worktree is holding the given ref")),
+               { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules,
+                           "checkout", "control recursive updating of submodules",
+                           PARSE_OPT_OPTARG, option_parse_recurse_submodules },
                OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
                OPT_END(),
        };
                git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
        }
  
+       if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
+               git_config(submodule_config, NULL);
+               if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
+                       set_config_update_recurse_submodules(recurse_submodules);
+       }
        if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
                die(_("-b, -B and --orphan are mutually exclusive"));
  
diff --combined builtin/grep.c
index a9e82dc975e007fc2a77240ea1f9dca0803ecf32,b17835aed65df04eb790ff3139203a7467a891db..3f3efa95de2570bcd4f51a29fc7936817b9cee23
@@@ -294,17 -294,17 +294,17 @@@ static int grep_cmd_config(const char *
        return st;
  }
  
 -static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
 +static void *lock_and_read_oid_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
  {
        void *data;
  
        grep_read_lock();
 -      data = read_sha1_file(sha1, type, size);
 +      data = read_sha1_file(oid->hash, type, size);
        grep_read_unlock();
        return data;
  }
  
 -static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
 +static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
                     const char *filename, int tree_name_len,
                     const char *path)
  {
  
  #ifndef NO_PTHREADS
        if (num_threads) {
 -              add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
 +              add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, oid);
                strbuf_release(&pathbuf);
                return 0;
        } else
                struct grep_source gs;
                int hit;
  
 -              grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
 +              grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, oid);
                strbuf_release(&pathbuf);
                hit = grep_source(opt, &gs);
  
@@@ -538,7 -538,7 +538,7 @@@ static int grep_submodule_launch(struc
        int status, i;
        const char *end_of_base;
        const char *name;
 -      struct work_item *w = opt->output_priv;
 +      struct strbuf child_output = STRBUF_INIT;
  
        end_of_base = strchr(gs->name, ':');
        if (gs->identifier && end_of_base)
         * child process.  A '0' indicates a hit, a '1' indicates no hit and
         * anything else is an error.
         */
 -      status = capture_command(&cp, &w->out, 0);
 +      status = capture_command(&cp, &child_output, 0);
        if (status && (status != 1)) {
                /* flush the buffer */
 -              write_or_die(1, w->out.buf, w->out.len);
 +              write_or_die(1, child_output.buf, child_output.len);
                die("process for submodule '%s' failed with exit code: %d",
                    gs->name, status);
        }
  
 +      opt->output(opt, child_output.buf, child_output.len);
 +      strbuf_release(&child_output);
        /* invert the return code to make a hit equal to 1 */
        return !status;
  }
@@@ -618,7 -616,7 +618,7 @@@ static int grep_submodule(struct grep_o
  {
        if (!is_submodule_initialized(path))
                return 0;
-       if (!is_submodule_populated(path)) {
+       if (!is_submodule_populated_gently(path, NULL)) {
                /*
                 * If searching history, check for the presense of the
                 * submodule's gitdir before skipping the submodule.
        } else
  #endif
        {
 -              struct work_item w;
 +              struct grep_source gs;
                int hit;
  
 -              grep_source_init(&w.source, GREP_SOURCE_SUBMODULE,
 +              grep_source_init(&gs, GREP_SOURCE_SUBMODULE,
                                 filename, path, sha1);
 -              strbuf_init(&w.out, 0);
 -              opt->output_priv = &w;
 -              hit = grep_submodule_launch(opt, &w.source);
 +              hit = grep_submodule_launch(opt, &gs);
  
 -              write_or_die(1, w.out.buf, w.out.len);
 -
 -              grep_source_clear(&w.source);
 -              strbuf_release(&w.out);
 +              grep_source_clear(&gs);
                return hit;
        }
  }
@@@ -687,7 -690,7 +687,7 @@@ static int grep_cache(struct grep_opt *
                            ce_skip_worktree(ce)) {
                                if (ce_stage(ce) || ce_intent_to_add(ce))
                                        continue;
 -                              hit |= grep_sha1(opt, ce->oid.hash, ce->name,
 +                              hit |= grep_oid(opt, &ce->oid, ce->name,
                                                 0, ce->name);
                        } else {
                                hit |= grep_file(opt, ce->name);
@@@ -747,7 -750,7 +747,7 @@@ static int grep_tree(struct grep_opt *o
                strbuf_add(base, entry.path, te_len);
  
                if (S_ISREG(entry.mode)) {
 -                      hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len,
 +                      hit |= grep_oid(opt, entry.oid, base->buf, tn_len,
                                         check_attr ? base->buf + tn_len : NULL);
                } else if (S_ISDIR(entry.mode)) {
                        enum object_type type;
                        void *data;
                        unsigned long size;
  
 -                      data = lock_and_read_sha1_file(entry.oid->hash, &type, &size);
 +                      data = lock_and_read_oid_file(entry.oid, &type, &size);
                        if (!data)
                                die(_("unable to read tree (%s)"),
                                    oid_to_hex(entry.oid));
@@@ -784,7 -787,7 +784,7 @@@ static int grep_object(struct grep_opt 
                       struct object *obj, const char *name, const char *path)
  {
        if (obj->type == OBJ_BLOB)
 -              return grep_sha1(opt, obj->oid.hash, name, 0, path);
 +              return grep_oid(opt, &obj->oid, name, 0, path);
        if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
                struct tree_desc tree;
                void *data;
@@@ -964,7 -967,6 +964,7 @@@ int cmd_grep(int argc, const char **arg
        int dummy;
        int use_index = 1;
        int pattern_type_arg = GREP_PATTERN_TYPE_UNSPECIFIED;
 +      int allow_revs;
  
        struct option options[] = {
                OPT_BOOL(0, "cached", &cached,
  
        compile_grep_patterns(&opt);
  
 -      /* Check revs and then paths */
 +      /*
 +       * We have to find "--" in a separate pass, because its presence
 +       * influences how we will parse arguments that come before it.
 +       */
 +      for (i = 0; i < argc; i++) {
 +              if (!strcmp(argv[i], "--")) {
 +                      seen_dashdash = 1;
 +                      break;
 +              }
 +      }
 +
 +      /*
 +       * Resolve any rev arguments. If we have a dashdash, then everything up
 +       * to it must resolve as a rev. If not, then we stop at the first
 +       * non-rev and assume everything else is a path.
 +       */
 +      allow_revs = use_index && !untracked;
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
 -              unsigned char sha1[20];
 +              struct object_id oid;
                struct object_context oc;
 -              /* Is it a rev? */
 -              if (!get_sha1_with_context(arg, 0, sha1, &oc)) {
 -                      struct object *object = parse_object_or_die(sha1, arg);
 -                      if (!seen_dashdash)
 -                              verify_non_filename(prefix, arg);
 -                      add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
 -                      continue;
 -              }
 +              struct object *object;
 +
                if (!strcmp(arg, "--")) {
                        i++;
 -                      seen_dashdash = 1;
 +                      break;
                }
 -              break;
 +
 +              if (!allow_revs) {
 +                      if (seen_dashdash)
 +                              die(_("--no-index or --untracked cannot be used with revs"));
 +                      break;
 +              }
 +
 +              if (get_sha1_with_context(arg, 0, oid.hash, &oc)) {
 +                      if (seen_dashdash)
 +                              die(_("unable to resolve revision: %s"), arg);
 +                      break;
 +              }
 +
 +              object = parse_object_or_die(oid.hash, arg);
 +              if (!seen_dashdash)
 +                      verify_non_filename(prefix, arg);
 +              add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
        }
  
 +      /*
 +       * Anything left over is presumed to be a path. But in the non-dashdash
 +       * "do what I mean" case, we verify and complain when that isn't true.
 +       */
 +      if (!seen_dashdash) {
 +              int j;
 +              for (j = i; j < argc; j++)
 +                      verify_filename(prefix, argv[j], j == i && allow_revs);
 +      }
 +
 +      parse_pathspec(&pathspec, 0,
 +                     PATHSPEC_PREFER_CWD |
 +                     (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0),
 +                     prefix, argv + i);
 +      pathspec.max_depth = opt.max_depth;
 +      pathspec.recursive = 1;
 +
  #ifndef NO_PTHREADS
        if (list.nr || cached || show_in_pager)
                num_threads = 0;
        }
  #endif
  
 -      /* The rest are paths */
 -      if (!seen_dashdash) {
 -              int j;
 -              for (j = i; j < argc; j++)
 -                      verify_filename(prefix, argv[j], j == i);
 -      }
 -
 -      parse_pathspec(&pathspec, 0,
 -                     PATHSPEC_PREFER_CWD |
 -                     (opt.max_depth != -1 ? PATHSPEC_MAXDEPTH_VALID : 0),
 -                     prefix, argv + i);
 -      pathspec.max_depth = opt.max_depth;
 -      pathspec.recursive = 1;
 -
        if (recurse_submodules) {
                gitmodules_config();
                compile_submodule_options(&opt, &pathspec, cached, untracked,
  
        if (!use_index || untracked) {
                int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
 -              if (list.nr)
 -                      die(_("--no-index or --untracked cannot be used with revs."));
                hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
        } else if (0 <= opt_exclude) {
                die(_("--[no-]exclude-standard cannot be used for tracked contents."));
index 15a5430c0028f45ff6265a821ff85aa9d8445230,86bafe1660dd8e227f1cb07681e0d3b678259c5b..be316bbbc898c9311c6ec82aa0cfbc808f04e8c9
@@@ -356,10 -356,12 +356,10 @@@ static void init_submodule(const char *
                        strbuf_addf(&remotesb, "remote.%s.url", remote);
                        free(remote);
  
 -                      if (git_config_get_string(remotesb.buf, &remoteurl))
 -                              /*
 -                               * The repository is its own
 -                               * authoritative upstream
 -                               */
 +                      if (git_config_get_string(remotesb.buf, &remoteurl)) {
 +                              warning(_("could not lookup configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
                                remoteurl = xgetcwd();
 +                      }
                        relurl = relative_url(remoteurl, url, NULL);
                        strbuf_release(&remotesb);
                        free(remoteurl);
@@@ -577,9 -579,7 +577,7 @@@ static int module_clone(int argc, cons
        const char *name = NULL, *url = NULL, *depth = NULL;
        int quiet = 0;
        int progress = 0;
-       FILE *submodule_dot_git;
        char *p, *path = NULL, *sm_gitdir;
-       struct strbuf rel_path = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
        struct string_list reference = STRING_LIST_INIT_NODUP;
        char *sm_alternate = NULL, *error_strategy = NULL;
                strbuf_reset(&sb);
        }
  
-       /* Write a .git file in the submodule to redirect to the superproject. */
-       strbuf_addf(&sb, "%s/.git", path);
-       if (safe_create_leading_directories_const(sb.buf) < 0)
-               die(_("could not create leading directories of '%s'"), sb.buf);
-       submodule_dot_git = fopen(sb.buf, "w");
-       if (!submodule_dot_git)
-               die_errno(_("cannot open file '%s'"), sb.buf);
-       fprintf_or_die(submodule_dot_git, "gitdir: %s\n",
-                      relative_path(sm_gitdir, path, &rel_path));
-       if (fclose(submodule_dot_git))
-               die(_("could not close file %s"), sb.buf);
-       strbuf_reset(&sb);
-       strbuf_reset(&rel_path);
+       /* Connect module worktree and git dir */
+       connect_work_tree_and_git_dir(path, sm_gitdir);
  
-       /* Redirect the worktree of the submodule in the superproject's config */
        p = git_pathdup_submodule(path, "config");
        if (!p)
                die(_("could not get submodule directory for '%s'"), path);
-       git_config_set_in_file(p, "core.worktree",
-                              relative_path(path, sm_gitdir, &rel_path));
  
        /* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
        git_config_get_string("submodule.alternateLocation", &sm_alternate);
        free(error_strategy);
  
        strbuf_release(&sb);
-       strbuf_release(&rel_path);
        free(sm_gitdir);
        free(path);
        free(p);
diff --combined dir.c
index 837ff965a470e74028fce6019f4584e55dcf445c,6f52af7abb39463051e69f9d7131f0ab299be256..f451bfa48c0a0e7905d4c2adf4e3e05a8d272a8a
--- 1/dir.c
--- 2/dir.c
+++ b/dir.c
@@@ -9,7 -9,6 +9,7 @@@
   */
  #include "cache.h"
  #include "dir.h"
 +#include "attr.h"
  #include "refs.h"
  #include "wildmatch.h"
  #include "pathspec.h"
@@@ -135,8 -134,7 +135,8 @@@ static size_t common_prefix_len(const s
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
 -                     PATHSPEC_EXCLUDE);
 +                     PATHSPEC_EXCLUDE |
 +                     PATHSPEC_ATTR);
  
        for (n = 0; n < pathspec->nr; n++) {
                size_t i = 0, len = 0, item_len;
@@@ -211,36 -209,6 +211,36 @@@ int within_depth(const char *name, int 
  #define DO_MATCH_DIRECTORY (1<<1)
  #define DO_MATCH_SUBMODULE (1<<2)
  
 +static int match_attrs(const char *name, int namelen,
 +                     const struct pathspec_item *item)
 +{
 +      int i;
 +
 +      git_check_attr(name, item->attr_check);
 +      for (i = 0; i < item->attr_match_nr; i++) {
 +              const char *value;
 +              int matched;
 +              enum attr_match_mode match_mode;
 +
 +              value = item->attr_check->items[i].value;
 +              match_mode = item->attr_match[i].match_mode;
 +
 +              if (ATTR_TRUE(value))
 +                      matched = (match_mode == MATCH_SET);
 +              else if (ATTR_FALSE(value))
 +                      matched = (match_mode == MATCH_UNSET);
 +              else if (ATTR_UNSET(value))
 +                      matched = (match_mode == MATCH_UNSPECIFIED);
 +              else
 +                      matched = (match_mode == MATCH_VALUE &&
 +                                 !strcmp(item->attr_match[i].value, value));
 +              if (!matched)
 +                      return 0;
 +      }
 +
 +      return 1;
 +}
 +
  /*
   * Does 'match' match the given name?
   * A match is found if
@@@ -293,9 -261,6 +293,9 @@@ static int match_pathspec_item(const st
            strncmp(item->match, name - prefix, item->prefix))
                return 0;
  
 +      if (item->attr_match_nr && !match_attrs(name, namelen, item))
 +              return 0;
 +
        /* If the match was just the prefix, we matched */
        if (!*match)
                return MATCHED_RECURSIVELY;
@@@ -374,8 -339,7 +374,8 @@@ static int do_match_pathspec(const stru
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
 -                     PATHSPEC_EXCLUDE);
 +                     PATHSPEC_EXCLUDE |
 +                     PATHSPEC_ATTR);
  
        if (!ps->nr) {
                if (!ps->recursive ||
@@@ -1397,8 -1361,7 +1397,8 @@@ static int simplify_away(const char *pa
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
 -                     PATHSPEC_EXCLUDE);
 +                     PATHSPEC_EXCLUDE |
 +                     PATHSPEC_ATTR);
  
        for (i = 0; i < pathspec->nr; i++) {
                const struct pathspec_item *item = &pathspec->items[i];
@@@ -2765,23 -2728,33 +2765,33 @@@ void untracked_cache_add_to_index(struc
  /* 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 gitfile_sb = STRBUF_INIT;
+       struct strbuf cfg_sb = STRBUF_INIT;
        struct strbuf rel_path = STRBUF_INIT;
-       char *git_dir = real_pathdup(git_dir_, 1);
-       char *work_tree = real_pathdup(work_tree_, 1);
+       char *git_dir, *work_tree;
  
-       /* Update gitfile */
-       strbuf_addf(&file_name, "%s/.git", work_tree);
-       write_file(file_name.buf, "gitdir: %s",
-                  relative_path(git_dir, work_tree, &rel_path));
+       /* Prepare .git file */
+       strbuf_addf(&gitfile_sb, "%s/.git", work_tree_);
+       if (safe_create_leading_directories_const(gitfile_sb.buf))
+               die(_("could not create directories for %s"), gitfile_sb.buf);
+       /* Prepare config file */
+       strbuf_addf(&cfg_sb, "%s/config", git_dir_);
+       if (safe_create_leading_directories_const(cfg_sb.buf))
+               die(_("could not create directories for %s"), cfg_sb.buf);
  
 -      git_dir = real_pathdup(git_dir_);
 -      work_tree = real_pathdup(work_tree_);
++      git_dir = real_pathdup(git_dir_, 1);
++      work_tree = real_pathdup(work_tree_, 1);
+       /* Write .git file */
+       write_file(gitfile_sb.buf, "gitdir: %s",
+                  relative_path(git_dir, 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",
+       git_config_set_in_file(cfg_sb.buf, "core.worktree",
                               relative_path(work_tree, git_dir, &rel_path));
  
-       strbuf_release(&file_name);
+       strbuf_release(&gitfile_sb);
+       strbuf_release(&cfg_sb);
        strbuf_release(&rel_path);
        free(work_tree);
        free(git_dir);
diff --combined submodule-config.c
index bb069bc09734f473598d78493c1c08b1b4bd85e2,3e8e380d985b1d0de8e7909d229aa9c95ef99f23..4f58491ddb0705ab56e238006199d35db19dfb5a
@@@ -234,6 -234,26 +234,26 @@@ int parse_fetch_recurse_submodules_arg(
        return parse_fetch_recurse(opt, arg, 1);
  }
  
+ static int parse_update_recurse(const char *opt, const char *arg,
+                               int die_on_error)
+ {
+       switch (git_config_maybe_bool(opt, arg)) {
+       case 1:
+               return RECURSE_SUBMODULES_ON;
+       case 0:
+               return RECURSE_SUBMODULES_OFF;
+       default:
+               if (die_on_error)
+                       die("bad %s argument: %s", opt, arg);
+               return RECURSE_SUBMODULES_ERROR;
+       }
+ }
+ int parse_update_recurse_submodules_arg(const char *opt, const char *arg)
+ {
+       return parse_update_recurse(opt, arg, 1);
+ }
  static int parse_push_recurse(const char *opt, const char *arg,
                               int die_on_error)
  {
@@@ -333,7 -353,7 +353,7 @@@ static int parse_config(const char *var
                         strcmp(value, "all") &&
                         strcmp(value, "none"))
                        warning("Invalid parameter '%s' for config option "
 -                                      "'submodule.%s.ignore'", value, var);
 +                                      "'submodule.%s.ignore'", value, name.buf);
                else {
                        free((void *) submodule->ignore);
                        submodule->ignore = xstrdup(value);
diff --combined submodule.c
index 3200b7bb2b2360c7efab86b680ed5d0cafa5e9d3,6e8825b451a0ff55fdf5be96d60277ee6b10dd55..040f4c2287190cca706f9cd007c5942ae5c2b117
@@@ -17,6 -17,7 +17,7 @@@
  #include "worktree.h"
  
  static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
+ static int config_update_recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
  static int parallel_jobs = 1;
  static struct string_list changed_submodule_paths = STRING_LIST_INIT_NODUP;
  static int initialized_fetch_ref_tips;
@@@ -234,15 -235,12 +235,12 @@@ int is_submodule_initialized(const cha
        return ret;
  }
  
- /*
-  * Determine if a submodule has been populated at a given 'path'
-  */
- int is_submodule_populated(const char *path)
+ int is_submodule_populated_gently(const char *path, int *return_error_code)
  {
        int ret = 0;
        char *gitdir = xstrfmt("%s/.git", path);
  
-       if (resolve_gitdir(gitdir))
+       if (resolve_gitdir_gently(gitdir, return_error_code))
                ret = 1;
  
        free(gitdir);
@@@ -358,6 -356,23 +356,23 @@@ static void print_submodule_summary(str
        strbuf_release(&sb);
  }
  
+ static void prepare_submodule_repo_env_no_git_dir(struct argv_array *out)
+ {
+       const char * const *var;
+       for (var = local_repo_env; *var; var++) {
+               if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
+                       argv_array_push(out, *var);
+       }
+ }
+ void prepare_submodule_repo_env(struct argv_array *out)
+ {
+       prepare_submodule_repo_env_no_git_dir(out);
+       argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
+                        DEFAULT_GIT_DIR_ENVIRONMENT);
+ }
  /* Helper function to display the submodule header line prior to the full
   * summary output. If it can locate the submodule objects directory it will
   * attempt to lookup both the left and right commits and put them into the
@@@ -545,6 -560,27 +560,27 @@@ void set_config_fetch_recurse_submodule
        config_fetch_recurse_submodules = value;
  }
  
+ void set_config_update_recurse_submodules(int value)
+ {
+       config_update_recurse_submodules = value;
+ }
+ int should_update_submodules(void)
+ {
+       return config_update_recurse_submodules == RECURSE_SUBMODULES_ON;
+ }
+ const struct submodule *submodule_from_ce(const struct cache_entry *ce)
+ {
+       if (!S_ISGITLINK(ce->ce_mode))
+               return NULL;
+       if (!should_update_submodules())
+               return NULL;
+       return submodule_from_path(null_sha1, ce->name);
+ }
  static int has_remote(const char *refname, const struct object_id *oid,
                      int flags, void *cb_data)
  {
        return ret;
  }
  
+ static const char *get_super_prefix_or_empty(void)
+ {
+       const char *s = get_super_prefix();
+       if (!s)
+               s = "";
+       return s;
+ }
+ static int submodule_has_dirty_index(const struct submodule *sub)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       prepare_submodule_repo_env_no_git_dir(&cp.env_array);
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "diff-index", "--quiet",
+                                  "--cached", "HEAD", NULL);
+       cp.no_stdin = 1;
+       cp.no_stdout = 1;
+       cp.dir = sub->path;
+       if (start_command(&cp))
+               die("could not recurse into submodule '%s'", sub->path);
+       return finish_command(&cp);
+ }
+ static void submodule_reset_index(const char *path)
+ {
+       struct child_process cp = CHILD_PROCESS_INIT;
+       prepare_submodule_repo_env_no_git_dir(&cp.env_array);
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       cp.dir = path;
+       argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
+                                  get_super_prefix_or_empty(), path);
+       argv_array_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
+       argv_array_push(&cp.args, EMPTY_TREE_SHA1_HEX);
+       if (run_command(&cp))
+               die("could not reset submodule index");
+ }
+ /**
+  * Moves a submodule at a given path from a given head to another new head.
+  * For edge cases (a submodule coming into existence or removing a submodule)
+  * pass NULL for old or new respectively.
+  */
+ int submodule_move_head(const char *path,
+                        const char *old,
+                        const char *new,
+                        unsigned flags)
+ {
+       int ret = 0;
+       struct child_process cp = CHILD_PROCESS_INIT;
+       const struct submodule *sub;
+       sub = submodule_from_path(null_sha1, path);
+       if (!sub)
+               die("BUG: could not get submodule information for '%s'", path);
+       if (old && !(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 (!submodule_uses_gitfile(path))
+                               absorb_git_dir_into_superproject("", path,
+                                       ABSORB_GITDIR_RECURSE_SUBMODULES);
+               } else {
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addf(&sb, "%s/modules/%s",
+                                   get_git_common_dir(), sub->name);
+                       connect_work_tree_and_git_dir(path, sb.buf);
+                       strbuf_release(&sb);
+                       /* make sure the index is clean as well */
+                       submodule_reset_index(path);
+               }
+       }
+       prepare_submodule_repo_env_no_git_dir(&cp.env_array);
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       cp.dir = path;
+       argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
+                       get_super_prefix_or_empty(), path);
+       argv_array_pushl(&cp.args, "read-tree", NULL);
+       if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
+               argv_array_push(&cp.args, "-n");
+       else
+               argv_array_push(&cp.args, "-u");
+       if (flags & SUBMODULE_MOVE_HEAD_FORCE)
+               argv_array_push(&cp.args, "--reset");
+       else
+               argv_array_push(&cp.args, "-m");
+       argv_array_push(&cp.args, old ? old : EMPTY_TREE_SHA1_HEX);
+       argv_array_push(&cp.args, new ? new : EMPTY_TREE_SHA1_HEX);
+       if (run_command(&cp)) {
+               ret = -1;
+               goto out;
+       }
+       if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
+               if (new) {
+                       struct child_process cp1 = CHILD_PROCESS_INIT;
+                       /* also set the HEAD accordingly */
+                       cp1.git_cmd = 1;
+                       cp1.no_stdin = 1;
+                       cp1.dir = path;
+                       argv_array_pushl(&cp1.args, "update-ref", "HEAD",
+                                        new ? new : EMPTY_TREE_SHA1_HEX, NULL);
+                       if (run_command(&cp1)) {
+                               ret = -1;
+                               goto out;
+                       }
+               } else {
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addf(&sb, "%s/.git", path);
+                       unlink_or_warn(sb.buf);
+                       strbuf_release(&sb);
+                       if (is_empty_dir(path))
+                               rmdir_or_warn(path);
+               }
+       }
+ out:
+       return ret;
+ }
  static int find_first_merges(struct object_array *result, const char *path,
                struct commit *a, struct commit *b)
  {
@@@ -1371,18 -1552,6 +1552,6 @@@ int parallel_submodules(void
        return parallel_jobs;
  }
  
- void prepare_submodule_repo_env(struct argv_array *out)
- {
-       const char * const *var;
-       for (var = local_repo_env; *var; var++) {
-               if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
-                       argv_array_push(out, *var);
-       }
-       argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
-                        DEFAULT_GIT_DIR_ENVIRONMENT);
- }
  /*
   * Embeds a single submodules git directory into the superprojects git dir,
   * non recursively.
@@@ -1403,7 -1572,7 +1572,7 @@@ static void relocate_single_git_dir_int
                /* If it is an actual gitfile, it doesn't need migration. */
                return;
  
 -      real_old_git_dir = real_pathdup(old_git_dir);
 +      real_old_git_dir = real_pathdup(old_git_dir, 1);
  
        sub = submodule_from_path(null_sha1, path);
        if (!sub)
        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_new_git_dir = real_pathdup(new_git_dir);
 +      real_new_git_dir = real_pathdup(new_git_dir, 1);
  
-       if (!prefix)
-               prefix = get_super_prefix();
        fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
-               prefix ? prefix : "", path,
+               get_super_prefix_or_empty(), path,
                real_old_git_dir, real_new_git_dir);
  
        relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
@@@ -1445,8 -1611,6 +1611,6 @@@ void absorb_git_dir_into_superproject(c
  
        /* Not populated? */
        if (!sub_git_dir) {
-               char *real_new_git_dir;
-               const char *new_git_dir;
                const struct submodule *sub;
  
                if (err_code == READ_GITFILE_ERR_STAT_FAILED) {
                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_new_git_dir = real_pathdup(new_git_dir, 1);
-               connect_work_tree_and_git_dir(path, real_new_git_dir);
-               free(real_new_git_dir);
+               connect_work_tree_and_git_dir(path,
+                       git_path("modules/%s", sub->name));
        } else {
                /* Is it already absorbed into the superprojects git dir? */
 -              char *real_sub_git_dir = real_pathdup(sub_git_dir);
 -              char *real_common_git_dir = real_pathdup(get_git_common_dir());
 +              char *real_sub_git_dir = real_pathdup(sub_git_dir, 1);
 +              char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1);
  
                if (!starts_with(real_sub_git_dir, real_common_git_dir))
                        relocate_single_git_dir_into_superproject(prefix, path);
                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, get_super_prefix_or_empty());
                strbuf_addstr(&sb, path);
                strbuf_addch(&sb, '/');
  
                strbuf_release(&sb);
        }
  }
 +
 +const char *get_superproject_working_tree(void)
 +{
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      struct strbuf sb = STRBUF_INIT;
 +      const char *one_up = real_path_if_valid("../");
 +      const char *cwd = xgetcwd();
 +      const char *ret = NULL;
 +      const char *subpath;
 +      int code;
 +      ssize_t len;
 +
 +      if (!is_inside_work_tree())
 +              /*
 +               * FIXME:
 +               * We might have a superproject, but it is harder
 +               * to determine.
 +               */
 +              return NULL;
 +
 +      if (!one_up)
 +              return NULL;
 +
 +      subpath = relative_path(cwd, one_up, &sb);
 +
 +      prepare_submodule_repo_env(&cp.env_array);
 +      argv_array_pop(&cp.env_array);
 +
 +      argv_array_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
 +                      "ls-files", "-z", "--stage", "--full-name", "--",
 +                      subpath, NULL);
 +      strbuf_reset(&sb);
 +
 +      cp.no_stdin = 1;
 +      cp.no_stderr = 1;
 +      cp.out = -1;
 +      cp.git_cmd = 1;
 +
 +      if (start_command(&cp))
 +              die(_("could not start ls-files in .."));
 +
 +      len = strbuf_read(&sb, cp.out, PATH_MAX);
 +      close(cp.out);
 +
 +      if (starts_with(sb.buf, "160000")) {
 +              int super_sub_len;
 +              int cwd_len = strlen(cwd);
 +              char *super_sub, *super_wt;
 +
 +              /*
 +               * There is a superproject having this repo as a submodule.
 +               * The format is <mode> SP <hash> SP <stage> TAB <full name> \0,
 +               * We're only interested in the name after the tab.
 +               */
 +              super_sub = strchr(sb.buf, '\t') + 1;
 +              super_sub_len = sb.buf + sb.len - super_sub - 1;
 +
 +              if (super_sub_len > cwd_len ||
 +                  strcmp(&cwd[cwd_len - super_sub_len], super_sub))
 +                      die (_("BUG: returned path string doesn't match cwd?"));
 +
 +              super_wt = xstrdup(cwd);
 +              super_wt[cwd_len - super_sub_len] = '\0';
 +
 +              ret = real_path(super_wt);
 +              free(super_wt);
 +      }
 +      strbuf_release(&sb);
 +
 +      code = finish_command(&cp);
 +
 +      if (code == 128)
 +              /* '../' is not a git repository */
 +              return NULL;
 +      if (code == 0 && len == 0)
 +              /* There is an unrelated git repository at '../' */
 +              return NULL;
 +      if (code)
 +              die(_("ls-tree returned unexpected return code %d"), code);
 +
 +      return ret;
 +}
diff --combined submodule.h
index c8a0c9cb2971219b807f7fe931c4dc5355b978ea,4cdf6445f7a6dd61316f4d5c05bf472dfb4c2518..8a8bc49dc9626b9ba4112f3b0bf25b212d864d1d
@@@ -41,7 -41,13 +41,13 @@@ extern int submodule_config(const char 
  extern 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);
+ /*
+  * Determine if a submodule has been populated at a given 'path' by checking if
+  * the <path>/.git resolves to a valid git repository.
+  * If return_error_code is NULL, die on error.
+  * Otherwise the return error code is the same as of resolve_gitdir_gently.
+  */
+ extern int is_submodule_populated_gently(const char *path, int *return_error_code);
  extern int parse_submodule_update_strategy(const char *value,
                struct submodule_update_strategy *dst);
  extern const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
@@@ -58,6 -64,14 +64,14 @@@ extern void show_submodule_inline_diff(
                const char *del, const char *add, const char *reset,
                const struct diff_options *opt);
  extern void set_config_fetch_recurse_submodules(int value);
+ extern void set_config_update_recurse_submodules(int value);
+ /* Check if we want to update any submodule.*/
+ extern int should_update_submodules(void);
+ /*
+  * Returns the submodule struct if the given ce entry is a submodule
+  * and it should be updated. Returns NULL otherwise.
+  */
+ extern const struct submodule *submodule_from_ce(const struct cache_entry *ce);
  extern void check_for_new_submodule_commits(unsigned char new_sha1[20]);
  extern int fetch_populated_submodules(const struct argv_array *options,
                               const char *prefix, int command_line_option,
@@@ -82,6 -96,13 +96,13 @@@ extern int push_unpushed_submodules(str
  extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
  extern int parallel_submodules(void);
  
+ #define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
+ #define SUBMODULE_MOVE_HEAD_FORCE   (1<<1)
+ extern int submodule_move_head(const char *path,
+                              const char *old,
+                              const char *new,
+                              unsigned flags);
  /*
   * Prepare the "env_array" parameter of a "struct child_process" for executing
   * a submodule by clearing any repo-specific envirionment variables, but
@@@ -93,12 -114,4 +114,12 @@@ extern void prepare_submodule_repo_env(
  extern void absorb_git_dir_into_superproject(const char *prefix,
                                             const char *path,
                                             unsigned flags);
 +
 +/*
 + * Return the absolute path of the working tree of the superproject, which this
 + * project is a submodule of. If this repository is not a submodule of
 + * another repository, return NULL.
 + */
 +extern const char *get_superproject_working_tree(void);
 +
  #endif