Merge branch 'jl/submodule-fetch-on-demand'
authorJunio C Hamano <gitster@pobox.com>
Mon, 4 Apr 2011 22:02:01 +0000 (15:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 4 Apr 2011 22:02:01 +0000 (15:02 -0700)
* jl/submodule-fetch-on-demand:
fetch/pull: Describe --recurse-submodule restrictions in the BUGS section
submodule update: Don't fetch when the submodule commit is already present
fetch/pull: Don't recurse into a submodule when commits are already present
Submodules: Add 'on-demand' value for the 'fetchRecurseSubmodule' option
config: teach the fetch.recurseSubmodules option the 'on-demand' value
fetch/pull: Add the 'on-demand' value to the --recurse-submodules option
fetch/pull: recurse into submodules when necessary

Conflicts:
builtin/fetch.c
submodule.c

1  2 
Documentation/config.txt
Documentation/git-fetch.txt
Documentation/git-pull.txt
Documentation/gitmodules.txt
builtin/fetch.c
git-pull.sh
git-submodule.sh
submodule.c
t/t5526-fetch-submodules.sh
t/t7406-submodule-update.sh
diff --combined Documentation/config.txt
index 1d0d1b76772cf33bd6dba110fef614e2e87a5f09,2210633882e06e00fb345154c6b132ab125c0c60..6d4dfa602e9ef76c0a08a7f613370ee7a982a362
@@@ -62,7 -62,7 +62,7 @@@ Internal whitespace within a variable v
  
  The values following the equals sign in variable assign are all either
  a string, an integer, or a boolean.  Boolean values may be given as yes/no,
 -0/1, true/false or on/off.  Case is not significant in boolean values, when
 +1/0, true/false or on/off.  Case is not significant in boolean values, when
  converting value to the canonical form using '--bool' type specifier;
  'git config' will ensure that the output is "true" or "false".
  
@@@ -376,6 -376,15 +376,6 @@@ core.warnAmbiguousRefs:
        If true, git will warn you if the ref name you passed it is ambiguous
        and might match multiple refs in the .git/refs/ tree. True by default.
  
 -core.abbrevguard::
 -      Even though git makes sure that it uses enough hexdigits to show
 -      an abbreviated object name unambiguously, as more objects are
 -      added to the repository over time, a short name that used to be
 -      unique will stop being unique.  Git uses this many extra hexdigits
 -      that are more than necessary to make the object name currently
 -      unique, in the hope that its output will stay unique a bit longer.
 -      Defaults to 0.
 -
  core.compression::
        An integer -1..9, indicating a default compression level.
        -1 is the zlib default. 0 means no compression,
@@@ -558,12 -567,6 +558,12 @@@ core.sparseCheckout:
        Enable "sparse checkout" feature. See section "Sparse checkout" in
        linkgit:git-read-tree[1] for more information.
  
 +core.abbrev::
 +      Set the length object names are abbreviated to.  If unspecified,
 +      many commands abbreviate to 7 hexdigits, which may not be enough
 +      for abbreviated object names to stay unique for sufficiently long
 +      time.
 +
  add.ignore-errors::
  add.ignoreErrors::
        Tells 'git add' to continue adding files when some files cannot be
@@@ -897,9 -900,13 +897,13 @@@ diff.wordRegex:
        characters are *ignorable* whitespace.
  
  fetch.recurseSubmodules::
-       A boolean value which changes the behavior for fetch and pull, the
-       default is to not recursively fetch populated submodules unless
-       configured otherwise.
+       This option can be either set to a boolean value or to 'on-demand'.
+       Setting it to a boolean changes the behavior of fetch and pull to
+       unconditionally recurse into submodules when set to true or to not
+       recurse at all when set to false. When set to 'on-demand' (the default
+       value), fetch and pull will only recurse into a populated submodule
+       when its superproject retrieves a commit that updates the submodule's
+       reference.
  
  fetch.unpackLimit::
        If the number of objects fetched over the git native
@@@ -1098,12 -1105,6 +1102,12 @@@ All gitcvs variables except for 'gitcvs
  is one of "ext" and "pserver") to make them apply only for the given
  access method.
  
 +grep.lineNumber::
 +      If set to true, enable '-n' option by default.
 +
 +grep.extendedRegexp::
 +      If set to true, enable '--extended-regexp' option by default.
 +
  gui.commitmsgwidth::
        Defines how wide the commit message window is in the
        linkgit:git-gui[1]. "75" is the default.
@@@ -1594,8 -1595,7 +1598,8 @@@ push.default:
  * `matching` - push all matching branches.
    All branches having the same name in both ends are considered to be
    matching. This is the default.
 -* `tracking` - push the current branch to its upstream branch.
 +* `upstream` - push the current branch to its upstream branch.
 +* `tracking` - deprecated synonym for `upstream`.
  * `current` - push the current branch to a branch of the same name.
  
  rebase.stat::
@@@ -1823,7 -1823,7 +1827,7 @@@ submodule.<name>.update:
        linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
  
  submodule.<name>.fetchRecurseSubmodules::
-       This option can be used to enable/disable recursive fetching of this
+       This option can be used to control recursive fetching of this
        submodule. It can be overridden by using the --[no-]recurse-submodules
        command line option to "git fetch" and "git pull".
        This setting will override that from in the linkgit:gitmodules[5]
index 7146f6ba0b3d531f788693cf78590565a0645144,86692276ba272f4a31e1c5510e00c8477e766a98..67d221467d6dba14286f96e9ce1442cbfc1ee995
@@@ -76,10 -76,29 +76,19 @@@ The `pu` branch will be updated even i
  because it is prefixed with a plus sign; `tmp` will not be.
  
  
+ BUGS
+ ----
+ Using --recurse-submodules can only fetch new commits in already checked
+ out submodules right now. When e.g. upstream added a new submodule in the
+ just fetched commits of the superproject the submodule itself can not be
+ fetched, making it impossible to check out that submodule later without
+ having to do a fetch again. This is expected to be fixed in a future git
+ version.
  SEE ALSO
  --------
  linkgit:git-pull[1]
  
 -
 -Author
 -------
 -Written by Linus Torvalds <torvalds@osdl.org> and
 -Junio C Hamano <gitster@pobox.com>
 -
 -Documentation
 --------------
 -Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
 -
  GIT
  ---
  Part of the linkgit:git[1] suite
index c2a7f103ee5bf4495d39b3034752be564b119766,1aad8bfc597c029d6804f1bac1d35d85622cd986..14609cbd4dc2f7f3c16f7b660e4aff76eca6065b
@@@ -84,7 -84,7 +84,7 @@@ must be given before the options meant 
  --verbose::
        Pass --verbose to git-fetch and git-merge.
  
- --[no-]recurse-submodules::
+ --[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
@@@ -220,10 -220,31 +220,19 @@@ If you tried a pull which resulted in 
  would want to start over, you can recover with 'git reset'.
  
  
+ BUGS
+ ----
+ Using --recurse-submodules can only fetch new commits in already checked
+ out submodules right now. When e.g. upstream added a new submodule in the
+ just fetched commits of the superproject the submodule itself can not be
+ fetched, making it impossible to check out that submodule later without
+ having to do a fetch again. This is expected to be fixed in a future git
+ version.
  SEE ALSO
  --------
  linkgit:git-fetch[1], linkgit:git-merge[1], linkgit:git-config[1]
  
 -
 -Author
 -------
 -Written by Linus Torvalds <torvalds@osdl.org>
 -and Junio C Hamano <gitster@pobox.com>
 -
 -Documentation
 ---------------
 -Documentation by Jon Loeliger,
 -David Greaves,
 -Junio C Hamano and the git-list <git@vger.kernel.org>.
 -
  GIT
  ---
  Part of the linkgit:git[1] suite
index 15a218655970de61781e1b2793576a05b0fac7b2,25daee221fabad13adca5d043e5c5770624569c7..4040941e55e88114b70d3d0ae7ecb644794fc8b3
@@@ -45,12 -45,12 +45,12 @@@ submodule.<name>.update:
        the '--merge' or '--rebase' options.
  
  submodule.<name>.fetchRecurseSubmodules::
-       This option can be used to enable/disable recursive fetching of this
+       This option can be used to control recursive fetching of this
        submodule. If this option is also present in the submodules entry in
        .git/config of the superproject, the setting there will override the
        one found in .gitmodules.
        Both settings can be overridden on the command line by using the
-       "--[no-]recurse-submodules" option to "git fetch" and "git pull"..
+       "--[no-]recurse-submodules" option to "git fetch" and "git pull".
  
  submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
@@@ -90,6 -90,10 +90,6 @@@ SEE ALS
  --------
  linkgit:git-submodule[1] linkgit:git-config[1]
  
 -DOCUMENTATION
 --------------
 -Documentation by Lars Hjemli <hjemli@gmail.com>
 -
  GIT
  ---
  Part of the linkgit:git[1] suite
diff --combined builtin/fetch.c
index 6cbb5f69ecf0946f4028b88563347332d54f65bc,f60393607691957fa3978bcc4e1f0b5676a74609..f9c41da475289b84fe849f7641406ebc94059630
@@@ -28,12 -28,6 +28,6 @@@ enum 
        TAGS_SET = 2
  };
  
- enum {
-       RECURSE_SUBMODULES_OFF = 0,
-       RECURSE_SUBMODULES_DEFAULT = 1,
-       RECURSE_SUBMODULES_ON = 2
- };
  static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
  static int progress, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
  static int tags = TAGS_DEFAULT;
@@@ -42,6 -36,21 +36,21 @@@ static const char *upload_pack
  static struct strbuf default_rla = STRBUF_INIT;
  static struct transport *transport;
  static const char *submodule_prefix = "";
+ static const char *recurse_submodules_default;
+ 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 struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
@@@ -49,7 -58,7 +58,7 @@@
                    "fetch from all remotes"),
        OPT_BOOLEAN('a', "append", &append,
                    "append to .git/FETCH_HEAD instead of overwriting"),
 -      OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
 +      OPT_STRING(0, "upload-pack", &upload_pack, "path",
                   "path to upload pack on remote end"),
        OPT__FORCE(&force, "force overwrite of local branch"),
        OPT_BOOLEAN('m', "multiple", &multiple,
                    "do not fetch all tags (--no-tags)", TAGS_UNSET),
        OPT_BOOLEAN('p', "prune", &prune,
                    "prune remote-tracking branches no longer on remote"),
-       OPT_SET_INT(0, "recurse-submodules", &recurse_submodules,
+       { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "on-demand",
                    "control recursive fetching of submodules",
-                   RECURSE_SUBMODULES_ON),
+                   PARSE_OPT_OPTARG, option_parse_recurse_submodules },
        OPT_BOOLEAN(0, "dry-run", &dry_run,
                    "dry run"),
        OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
        OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
                    "allow updating of HEAD ref"),
        OPT_BOOLEAN(0, "progress", &progress, "force progress reporting"),
 -      OPT_STRING(0, "depth", &depth, "DEPTH",
 +      OPT_STRING(0, "depth", &depth, "depth",
                   "deepen history of shallow clone"),
 -      { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "DIR",
 +      { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "dir",
                   "prepend this to submodule path output", PARSE_OPT_HIDDEN },
+       { OPTION_STRING, 0, "recurse-submodules-default",
+                  &recurse_submodules_default, NULL,
+                  "default mode for recursion", PARSE_OPT_HIDDEN },
        OPT_END()
  };
  
@@@ -184,7 -196,7 +196,7 @@@ static struct ref *get_ref_map(struct t
                } else {
                        ref_map = get_remote_ref(remote_refs, "HEAD");
                        if (!ref_map)
 -                              die("Couldn't find remote ref HEAD");
 +                              die(_("Couldn't find remote ref HEAD"));
                        ref_map->merge = 1;
                        tail = &ref_map->next;
                }
@@@ -237,12 -249,12 +249,12 @@@ static int update_local_ref(struct ref 
        *display = 0;
        type = sha1_object_info(ref->new_sha1, NULL);
        if (type < 0)
 -              die("object %s not found", sha1_to_hex(ref->new_sha1));
 +              die(_("object %s not found"), sha1_to_hex(ref->new_sha1));
  
        if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
                if (verbosity > 0)
                        sprintf(display, "= %-*s %-*s -> %s", TRANSPORT_SUMMARY_WIDTH,
 -                              "[up to date]", REFCOL_WIDTH, remote,
 +                              _("[up to date]"), REFCOL_WIDTH, remote,
                                pretty_ref);
                return 0;
        }
                 * If this is the head, and it's not okay to update
                 * the head, and the old value of the head isn't empty...
                 */
 -              sprintf(display, "! %-*s %-*s -> %s  (can't fetch in current branch)",
 -                      TRANSPORT_SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
 +              sprintf(display, _("! %-*s %-*s -> %s  (can't fetch in current branch)"),
 +                      TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote,
                        pretty_ref);
                return 1;
        }
                int r;
                r = s_update_ref("updating tag", ref, 0);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '-',
 -                      TRANSPORT_SUMMARY_WIDTH, "[tag update]", REFCOL_WIDTH, remote,
 -                      pretty_ref, r ? "  (unable to update local ref)" : "");
 +                      TRANSPORT_SUMMARY_WIDTH, _("[tag update]"), REFCOL_WIDTH, remote,
 +                      pretty_ref, r ? _("  (unable to update local ref)") : "");
                return r;
        }
  
                int r;
                if (!strncmp(ref->name, "refs/tags/", 10)) {
                        msg = "storing tag";
 -                      what = "[new tag]";
 +                      what = _("[new tag]");
                }
                else {
                        msg = "storing head";
 -                      what = "[new branch]";
 +                      what = _("[new branch]");
+                       if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
+                           (recurse_submodules != RECURSE_SUBMODULES_ON))
+                               check_for_new_submodule_commits(ref->new_sha1);
                }
  
                r = s_update_ref(msg, ref, 0);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : '*',
                        TRANSPORT_SUMMARY_WIDTH, what, REFCOL_WIDTH, remote, pretty_ref,
 -                      r ? "  (unable to update local ref)" : "");
 +                      r ? _("  (unable to update local ref)") : "");
                return r;
        }
  
                strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
                strcat(quickref, "..");
                strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
+                   (recurse_submodules != RECURSE_SUBMODULES_ON))
+                       check_for_new_submodule_commits(ref->new_sha1);
                r = s_update_ref("fast-forward", ref, 1);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
                        TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
 -                      pretty_ref, r ? "  (unable to update local ref)" : "");
 +                      pretty_ref, r ? _("  (unable to update local ref)") : "");
                return r;
        } else if (force || ref->force) {
                char quickref[84];
                strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
                strcat(quickref, "...");
                strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
+                   (recurse_submodules != RECURSE_SUBMODULES_ON))
+                       check_for_new_submodule_commits(ref->new_sha1);
                r = s_update_ref("forced-update", ref, 1);
                sprintf(display, "%c %-*s %-*s -> %s  (%s)", r ? '!' : '+',
                        TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
                        pretty_ref,
 -                      r ? "unable to update local ref" : "forced update");
 +                      r ? _("unable to update local ref") : _("forced update"));
                return r;
        } else {
 -              sprintf(display, "! %-*s %-*s -> %s  (non-fast-forward)",
 -                      TRANSPORT_SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
 -                      pretty_ref);
 +              sprintf(display, "! %-*s %-*s -> %s  %s",
 +                      TRANSPORT_SUMMARY_WIDTH, _("[rejected]"), REFCOL_WIDTH, remote,
 +                      pretty_ref, _("(non-fast-forward)"));
                return 1;
        }
  }
@@@ -337,7 -358,7 +358,7 @@@ static int store_updated_refs(const cha
  
        fp = fopen(filename, "a");
        if (!fp)
 -              return error("cannot open %s: %s\n", filename, strerror(errno));
 +              return error(_("cannot open %s: %s\n"), filename, strerror(errno));
  
        if (raw_url)
                url = transport_anonymize_url(raw_url);
                                 REFCOL_WIDTH, *what ? what : "HEAD");
                if (*note) {
                        if (verbosity >= 0 && !shown_url) {
 -                              fprintf(stderr, "From %.*s\n",
 +                              fprintf(stderr, _("From %.*s\n"),
                                                url_len, url);
                                shown_url = 1;
                        }
        free(url);
        fclose(fp);
        if (rc & STORE_REF_ERROR_DF_CONFLICT)
 -              error("some local refs could not be updated; try running\n"
 +              error(_("some local refs could not be updated; try running\n"
                      " 'git remote prune %s' to remove any old, conflicting "
 -                    "branches", remote_name);
 +                    "branches"), remote_name);
        return rc;
  }
  
@@@ -476,7 -497,7 +497,7 @@@ static int quickfetch(struct ref *ref_m
  
        err = start_command(&revlist);
        if (err) {
 -              error("could not run rev-list");
 +              error(_("could not run rev-list"));
                return err;
        }
  
                if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 ||
                    write_str_in_full(revlist.in, "\n") < 0) {
                        if (errno != EPIPE && errno != EINVAL)
 -                              error("failed write to rev-list: %s", strerror(errno));
 +                              error(_("failed write to rev-list: %s"), strerror(errno));
                        err = -1;
                        break;
                }
        }
  
        if (close(revlist.in)) {
 -              error("failed to close rev-list's stdin: %s", strerror(errno));
 +              error(_("failed to close rev-list's stdin: %s"), strerror(errno));
                err = -1;
        }
  
@@@ -524,16 -545,16 +545,16 @@@ static int prune_refs(struct transport 
        int result = 0;
        struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map);
        const char *dangling_msg = dry_run
 -              ? "   (%s will become dangling)\n"
 -              : "   (%s has become dangling)\n";
 +              ? _("   (%s will become dangling)\n")
 +              : _("   (%s has become dangling)\n");
  
        for (ref = stale_refs; ref; ref = ref->next) {
                if (!dry_run)
                        result |= delete_ref(ref->name, NULL, 0);
                if (verbosity >= 0) {
                        fprintf(stderr, " x %-*s %-*s -> %s\n",
 -                              TRANSPORT_SUMMARY_WIDTH, "[deleted]",
 -                              REFCOL_WIDTH, "(none)", prettify_refname(ref->name));
 +                              TRANSPORT_SUMMARY_WIDTH, _("[deleted]"),
 +                              REFCOL_WIDTH, _("(none)"), prettify_refname(ref->name));
                        warn_dangling_symref(stderr, dangling_msg, ref->name);
                }
        }
@@@ -650,8 -671,8 +671,8 @@@ static void check_not_current_branch(st
        for (; ref_map; ref_map = ref_map->next)
                if (ref_map->peer_ref && !strcmp(current_branch->refname,
                                        ref_map->peer_ref->name))
 -                      die("Refusing to fetch into current branch %s "
 -                          "of non-bare repository", current_branch->refname);
 +                      die(_("Refusing to fetch into current branch %s "
 +                          "of non-bare repository"), current_branch->refname);
  }
  
  static int truncate_fetch_head(void)
        FILE *fp = fopen(filename, "w");
  
        if (!fp)
 -              return error("cannot open %s: %s\n", filename, strerror(errno));
 +              return error(_("cannot open %s: %s\n"), filename, strerror(errno));
        fclose(fp);
        return 0;
  }
@@@ -684,7 -705,7 +705,7 @@@ static int do_fetch(struct transport *t
        }
  
        if (!transport->get_refs_list || !transport->fetch)
 -              die("Don't know how to fetch from %s", transport->url);
 +              die(_("Don't know how to fetch from %s"), transport->url);
  
        /* if not appending, truncate FETCH_HEAD */
        if (!append && !dry_run) {
@@@ -738,10 -759,10 +759,10 @@@ static void set_option(const char *name
  {
        int r = transport_set_option(transport, name, value);
        if (r < 0)
 -              die("Option \"%s\" value \"%s\" is not valid for %s",
 +              die(_("Option \"%s\" value \"%s\" is not valid for %s"),
                        name, value, transport->url);
        if (r > 0)
 -              warning("Option \"%s\" is ignored for %s\n",
 +              warning(_("Option \"%s\" is ignored for %s\n"),
                        name, transport->url);
  }
  
@@@ -810,6 -831,8 +831,8 @@@ static void add_options_to_argv(int *ar
                argv[(*argc)++] = "--keep";
        if (recurse_submodules == RECURSE_SUBMODULES_ON)
                argv[(*argc)++] = "--recurse-submodules";
+       else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
+               argv[(*argc)++] = "--recurse-submodules=on-demand";
        if (verbosity >= 2)
                argv[(*argc)++] = "-v";
        if (verbosity >= 1)
@@@ -838,9 -861,9 +861,9 @@@ static int fetch_multiple(struct string
                argv[argc] = name;
                argv[argc + 1] = NULL;
                if (verbosity >= 0)
 -                      printf("Fetching %s\n", name);
 +                      printf(_("Fetching %s\n"), name);
                if (run_command_v_opt(argv, RUN_GIT_CMD)) {
 -                      error("Could not fetch %s", name);
 +                      error(_("Could not fetch %s"), name);
                        result = 1;
                }
        }
@@@ -856,8 -879,8 +879,8 @@@ static int fetch_one(struct remote *rem
        int exit_code;
  
        if (!remote)
 -              die("No remote repository specified.  Please, specify either a URL or a\n"
 -                  "remote name from which new revisions should be fetched.");
 +              die(_("No remote repository specified.  Please, specify either a URL or a\n"
 +                  "remote name from which new revisions should be fetched."));
  
        transport = transport_get(remote, NULL);
        transport_set_verbosity(transport, verbosity, progress);
                                char *ref;
                                i++;
                                if (i >= argc)
 -                                      die("You need to specify a tag name.");
 +                                      die(_("You need to specify a tag name."));
                                ref = xmalloc(strlen(argv[i]) * 2 + 22);
                                strcpy(ref, "refs/tags/");
                                strcat(ref, argv[i]);
@@@ -906,8 -929,6 +929,8 @@@ int cmd_fetch(int argc, const char **ar
        struct remote *remote;
        int result = 0;
  
 +      packet_trace_identity("fetch");
 +
        /* Record the command line for the reflog */
        strbuf_addstr(&default_rla, "fetch");
        for (i = 1; i < argc; i++)
  
        if (all) {
                if (argc == 1)
 -                      die("fetch --all does not take a repository argument");
 +                      die(_("fetch --all does not take a repository argument"));
                else if (argc > 1)
 -                      die("fetch --all does not make sense with refspecs");
 +                      die(_("fetch --all does not make sense with refspecs"));
                (void) for_each_remote(get_one_remote_for_fetch, &list);
                result = fetch_multiple(&list);
        } else if (argc == 0) {
                /* All arguments are assumed to be remotes or groups */
                for (i = 0; i < argc; i++)
                        if (!add_remote_or_group(argv[i], &list))
 -                              die("No such remote or remote group: %s", argv[i]);
 +                              die(_("No such remote or remote group: %s"), argv[i]);
                result = fetch_multiple(&list);
        } else {
                /* Single remote or group */
                if (list.nr > 1) {
                        /* More than one remote */
                        if (argc > 1)
 -                              die("Fetching a group and specifying refspecs does not make sense");
 +                              die(_("Fetching a group and specifying refspecs does not make sense"));
                        result = fetch_multiple(&list);
                } else {
                        /* Zero or one remotes */
        if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
                const char *options[10];
                int num_options = 0;
-               /* Set recursion as default when we already are recursing */
-               if (submodule_prefix[0])
-                       set_config_fetch_recurse_submodules(1);
+               if (recurse_submodules_default) {
+                       int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
+                       set_config_fetch_recurse_submodules(arg);
+               }
                gitmodules_config();
                git_config(submodule_config, NULL);
                add_options_to_argv(&num_options, options);
                result = fetch_populated_submodules(num_options, options,
                                                    submodule_prefix,
-                                                   recurse_submodules == RECURSE_SUBMODULES_ON,
+                                                   recurse_submodules,
                                                    verbosity < 0);
        }
  
diff --combined git-pull.sh
index e31226b62fb8b50d918b1e418bb1607f24070bd0,3fd634de4a5dff0b797350d96cc477d113bfdc14..4e9e0e49ecf2532ac8af1bfd46033e68c6f1042d
@@@ -53,8 -53,6 +53,8 @@@ d
                verbosity="$verbosity -v" ;;
        --progress)
                progress=--progress ;;
 +      --no-progress)
 +              progress=--no-progress ;;
        -n|--no-stat|--no-summary)
                diffstat=--no-stat ;;
        --stat|--summary)
        --recurse-submodules)
                recurse_submodules=--recurse-submodules
                ;;
+       --recurse-submodules=*)
+               recurse_submodules="$1"
+               ;;
        --no-recurse-submodules)
                recurse_submodules=--no-recurse-submodules
                ;;
@@@ -274,7 -275,7 +277,7 @@@ esa
  if test -z "$orig_head"
  then
        git update-ref -m "initial pull" HEAD $merge_head "$curr_head" &&
 -      git read-tree --reset -u HEAD || exit 1
 +      git read-tree -m -u HEAD || exit 1
        exit
  fi
  
@@@ -295,8 -296,8 +298,8 @@@ true
        ;;
  *)
        eval="git-merge $diffstat $no_commit $squash $no_ff $ff_only"
 -      eval="$eval  $log_arg $strategy_args $merge_args"
 -      eval="$eval \"\$merge_name\" HEAD $merge_head $verbosity"
 +      eval="$eval  $log_arg $strategy_args $merge_args $verbosity $progress"
 +      eval="$eval \"\$merge_name\" HEAD $merge_head"
        ;;
  esac
  eval "exec $eval"
diff --combined git-submodule.sh
index 7f6b3cf207ae51be30fc677cb8810ffb20cc9433,514d2eb7ab93c7d34e93d0c6dfe3c70a5d15c116..b010a673097a9cfcf009b307114669b6221d066c
@@@ -72,24 -72,7 +72,24 @@@ resolve_relative_url (
  #
  module_list()
  {
 -      git ls-files --error-unmatch --stage -- "$@" | sane_grep '^160000 '
 +      git ls-files --error-unmatch --stage -- "$@" |
 +      perl -e '
 +      my %unmerged = ();
 +      my ($null_sha1) = ("0" x 40);
 +      while (<STDIN>) {
 +              chomp;
 +              my ($mode, $sha1, $stage, $path) =
 +                      /^([0-7]+) ([0-9a-f]{40}) ([0-3])\t(.*)$/;
 +              next unless $mode eq "160000";
 +              if ($stage ne "0") {
 +                      if (!$unmerged{$path}++) {
 +                              print "$mode $null_sha1 U\t$path\n";
 +                      }
 +                      next;
 +              }
 +              print "$_\n";
 +      }
 +      '
  }
  
  #
@@@ -440,15 -423,9 +440,15 @@@ cmd_update(
                cmd_init "--" "$@" || return
        fi
  
 +      cloned_modules=
        module_list "$@" |
        while read mode sha1 stage path
        do
 +              if test "$stage" = U
 +              then
 +                      echo >&2 "Skipping unmerged submodule $path"
 +                      continue
 +              fi
                name=$(module_name "$path") || exit
                url=$(git config submodule."$name".url)
                update_module=$(git config submodule."$name".update)
                if ! test -d "$path"/.git -o -f "$path"/.git
                then
                        module_clone "$path" "$url" "$reference"|| exit
 +                      cloned_modules="$cloned_modules;$name"
                        subsha1=
                else
                        subsha1=$(clear_local_git_env; cd "$path" &&
  
                        if test -z "$nofetch"
                        then
+                               # Run fetch only if $sha1 isn't present or it
+                               # is not reachable from a ref.
                                (clear_local_git_env; cd "$path" &&
-                                       git-fetch) ||
+                                       ((rev=$(git rev-list -n 1 $sha1 --not --all 2>/dev/null) &&
+                                        test -z "$rev") || git-fetch)) ||
                                die "Unable to fetch in submodule path '$path'"
                        fi
  
 +                      # Is this something we just cloned?
 +                      case ";$cloned_modules;" in
 +                      *";$name;"*)
 +                              # then there is no local change to integrate
 +                              update_module= ;;
 +                      esac
 +
                        case "$update_module" in
                        rebase)
                                command="git rebase"
@@@ -792,11 -764,6 +795,11 @@@ cmd_status(
                name=$(module_name "$path") || exit
                url=$(git config submodule."$name".url)
                displaypath="$prefix$path"
 +              if test "$stage" = U
 +              then
 +                      say "U$sha1 $displaypath"
 +                      continue
 +              fi
                if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
                then
                        say "-$sha1 $displaypath"
diff --combined submodule.c
index 0cb6d1829944b8a6d0f1a7ed1c228b61c31c7e0d,88c7488a63bf2a3280cdc536609a1d2b15541d0c..5294cef641ef74ec525d8d747c82faf0bef7352a
@@@ -9,10 -9,11 +9,11 @@@
  #include "refs.h"
  #include "string-list.h"
  
 -struct string_list config_name_for_path;
 -struct string_list config_fetch_recurse_submodules_for_name;
 -struct string_list config_ignore_for_name;
 +static struct string_list config_name_for_path;
 +static struct string_list config_fetch_recurse_submodules_for_name;
 +static struct string_list config_ignore_for_name;
- static int config_fetch_recurse_submodules;
+ static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
 -struct string_list changed_submodule_paths;
++static struct string_list changed_submodule_paths;
  
  static int add_submodule_odb(const char *path)
  {
@@@ -70,7 -71,7 +71,7 @@@ int submodule_config(const char *var, c
        if (!prefixcmp(var, "submodule."))
                return parse_submodule_config_option(var, value);
        else if (!strcmp(var, "fetch.recursesubmodules")) {
-               config_fetch_recurse_submodules = git_config_bool(var, value);
+               config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
                return 0;
        }
        return 0;
@@@ -112,7 -113,7 +113,7 @@@ int parse_submodule_config_option(cons
                if (!config)
                        config = string_list_append(&config_fetch_recurse_submodules_for_name,
                                                    strbuf_detach(&submodname, NULL));
-               config->util = git_config_bool(var, value) ? (void *)1 : NULL;
+               config->util = (void *)(intptr_t)parse_fetch_recurse_submodules_arg(var, value);
                strbuf_release(&submodname);
        } else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
                if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
@@@ -152,69 -153,31 +153,83 @@@ void handle_ignore_submodules_arg(struc
                die("bad --ignore-submodules argument: %s", arg);
  }
  
 +static int prepare_submodule_summary(struct rev_info *rev, const char *path,
 +              struct commit *left, struct commit *right,
 +              int *fast_forward, int *fast_backward)
 +{
 +      struct commit_list *merge_bases, *list;
 +
 +      init_revisions(rev, NULL);
 +      setup_revisions(0, NULL, rev, NULL);
 +      rev->left_right = 1;
 +      rev->first_parent_only = 1;
 +      left->object.flags |= SYMMETRIC_LEFT;
 +      add_pending_object(rev, &left->object, path);
 +      add_pending_object(rev, &right->object, path);
 +      merge_bases = get_merge_bases(left, right, 1);
 +      if (merge_bases) {
 +              if (merge_bases->item == left)
 +                      *fast_forward = 1;
 +              else if (merge_bases->item == right)
 +                      *fast_backward = 1;
 +      }
 +      for (list = merge_bases; list; list = list->next) {
 +              list->item->object.flags |= UNINTERESTING;
 +              add_pending_object(rev, &list->item->object,
 +                      sha1_to_hex(list->item->object.sha1));
 +      }
 +      return prepare_revision_walk(rev);
 +}
 +
 +static void print_submodule_summary(struct rev_info *rev, FILE *f,
 +              const char *del, const char *add, const char *reset)
 +{
 +      static const char format[] = "  %m %s";
 +      struct strbuf sb = STRBUF_INIT;
 +      struct commit *commit;
 +
 +      while ((commit = get_revision(rev))) {
 +              struct pretty_print_context ctx = {0};
 +              ctx.date_mode = rev->date_mode;
 +              strbuf_setlen(&sb, 0);
 +              if (commit->object.flags & SYMMETRIC_LEFT) {
 +                      if (del)
 +                              strbuf_addstr(&sb, del);
 +              }
 +              else if (add)
 +                      strbuf_addstr(&sb, add);
 +              format_commit_message(commit, format, &sb, &ctx);
 +              if (reset)
 +                      strbuf_addstr(&sb, reset);
 +              strbuf_addch(&sb, '\n');
 +              fprintf(f, "%s", sb.buf);
 +      }
 +      strbuf_release(&sb);
 +}
 +
+ int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
+ {
+       switch (git_config_maybe_bool(opt, arg)) {
+       case 1:
+               return RECURSE_SUBMODULES_ON;
+       case 0:
+               return RECURSE_SUBMODULES_OFF;
+       default:
+               if (!strcmp(arg, "on-demand"))
+                       return RECURSE_SUBMODULES_ON_DEMAND;
+               die("bad %s argument: %s", opt, arg);
+       }
+ }
  void show_submodule_summary(FILE *f, const char *path,
                unsigned char one[20], unsigned char two[20],
                unsigned dirty_submodule,
                const char *del, const char *add, const char *reset)
  {
        struct rev_info rev;
 -      struct commit *commit, *left = left, *right = right;
 -      struct commit_list *merge_bases, *list;
 +      struct commit *left = left, *right = right;
        const char *message = NULL;
        struct strbuf sb = STRBUF_INIT;
 -      static const char *format = "  %m %s";
        int fast_forward = 0, fast_backward = 0;
  
        if (is_null_sha1(two))
                 !(right = lookup_commit_reference(two)))
                message = "(commits not present)";
  
 -      if (!message) {
 -              init_revisions(&rev, NULL);
 -              setup_revisions(0, NULL, &rev, NULL);
 -              rev.left_right = 1;
 -              rev.first_parent_only = 1;
 -              left->object.flags |= SYMMETRIC_LEFT;
 -              add_pending_object(&rev, &left->object, path);
 -              add_pending_object(&rev, &right->object, path);
 -              merge_bases = get_merge_bases(left, right, 1);
 -              if (merge_bases) {
 -                      if (merge_bases->item == left)
 -                              fast_forward = 1;
 -                      else if (merge_bases->item == right)
 -                              fast_backward = 1;
 -              }
 -              for (list = merge_bases; list; list = list->next) {
 -                      list->item->object.flags |= UNINTERESTING;
 -                      add_pending_object(&rev, &list->item->object,
 -                              sha1_to_hex(list->item->object.sha1));
 -              }
 -              if (prepare_revision_walk(&rev))
 -                      message = "(revision walker failed)";
 -      }
 +      if (!message &&
 +          prepare_submodule_summary(&rev, path, left, right,
 +                                      &fast_forward, &fast_backward))
 +              message = "(revision walker failed)";
  
        if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
                fprintf(f, "Submodule %s contains untracked content\n", path);
        fwrite(sb.buf, sb.len, 1, f);
  
        if (!message) {
 -              while ((commit = get_revision(&rev))) {
 -                      struct pretty_print_context ctx = {0};
 -                      ctx.date_mode = rev.date_mode;
 -                      strbuf_setlen(&sb, 0);
 -                      if (commit->object.flags & SYMMETRIC_LEFT) {
 -                              if (del)
 -                                      strbuf_addstr(&sb, del);
 -                      }
 -                      else if (add)
 -                              strbuf_addstr(&sb, add);
 -                      format_commit_message(commit, format, &sb, &ctx);
 -                      if (reset)
 -                              strbuf_addstr(&sb, reset);
 -                      strbuf_addch(&sb, '\n');
 -                      fprintf(f, "%s", sb.buf);
 -              }
 +              print_submodule_summary(&rev, f, del, add, reset);
                clear_commit_marks(left, ~0);
                clear_commit_marks(right, ~0);
        }
 +
        strbuf_release(&sb);
  }
  
@@@ -267,27 -263,122 +282,122 @@@ void set_config_fetch_recurse_submodule
        config_fetch_recurse_submodules = value;
  }
  
+ static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
+ {
+       int is_present = 0;
+       if (!add_submodule_odb(path) && lookup_commit_reference(sha1)) {
+               /* Even if the submodule is checked out and the commit is
+                * present, make sure it is reachable from a ref. */
+               struct child_process cp;
+               const char *argv[] = {"rev-list", "-n", "1", NULL, "--not", "--all", NULL};
+               struct strbuf buf = STRBUF_INIT;
+               argv[3] = sha1_to_hex(sha1);
+               memset(&cp, 0, sizeof(cp));
+               cp.argv = argv;
+               cp.env = local_repo_env;
+               cp.git_cmd = 1;
+               cp.no_stdin = 1;
+               cp.out = -1;
+               cp.dir = path;
+               if (!run_command(&cp) && !strbuf_read(&buf, cp.out, 1024))
+                       is_present = 1;
+               close(cp.out);
+               strbuf_release(&buf);
+       }
+       return is_present;
+ }
+ static void submodule_collect_changed_cb(struct diff_queue_struct *q,
+                                        struct diff_options *options,
+                                        void *data)
+ {
+       int i;
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               if (!S_ISGITLINK(p->two->mode))
+                       continue;
+               if (S_ISGITLINK(p->one->mode)) {
+                       /* NEEDSWORK: We should honor the name configured in
+                        * the .gitmodules file of the commit we are examining
+                        * here to be able to correctly follow submodules
+                        * being moved around. */
+                       struct string_list_item *path;
+                       path = unsorted_string_list_lookup(&changed_submodule_paths, p->two->path);
+                       if (!path && !is_submodule_commit_present(p->two->path, p->two->sha1))
+                               string_list_append(&changed_submodule_paths, xstrdup(p->two->path));
+               } else {
+                       /* Submodule is new or was moved here */
+                       /* NEEDSWORK: When the .git directories of submodules
+                        * live inside the superprojects .git directory some
+                        * day we should fetch new submodules directly into
+                        * that location too when config or options request
+                        * that so they can be checked out from there. */
+                       continue;
+               }
+       }
+ }
+ void check_for_new_submodule_commits(unsigned char new_sha1[20])
+ {
+       struct rev_info rev;
+       struct commit *commit;
+       const char *argv[] = {NULL, NULL, "--not", "--all", NULL};
+       int argc = ARRAY_SIZE(argv) - 1;
+       init_revisions(&rev, NULL);
+       argv[1] = xstrdup(sha1_to_hex(new_sha1));
+       setup_revisions(argc, argv, &rev, NULL);
+       if (prepare_revision_walk(&rev))
+               die("revision walk setup failed");
+       /*
+        * Collect all submodules (whether checked out or not) for which new
+        * commits have been recorded upstream in "changed_submodule_paths".
+        */
+       while ((commit = get_revision(&rev))) {
+               struct commit_list *parent = commit->parents;
+               while (parent) {
+                       struct diff_options diff_opts;
+                       diff_setup(&diff_opts);
+                       diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
+                       diff_opts.format_callback = submodule_collect_changed_cb;
+                       if (diff_setup_done(&diff_opts) < 0)
+                               die("diff_setup_done failed");
+                       diff_tree_sha1(parent->item->object.sha1, commit->object.sha1, "", &diff_opts);
+                       diffcore_std(&diff_opts);
+                       diff_flush(&diff_opts);
+                       parent = parent->next;
+               }
+       }
+       free((char *)argv[1]);
+ }
  int fetch_populated_submodules(int num_options, const char **options,
-                              const char *prefix, int ignore_config,
+                              const char *prefix, int command_line_option,
                               int quiet)
  {
-       int i, result = 0, argc = 0;
+       int i, result = 0, argc = 0, default_argc;
        struct child_process cp;
        const char **argv;
        struct string_list_item *name_for_path;
        const char *work_tree = get_git_work_tree();
        if (!work_tree)
-               return 0;
+               goto out;
  
        if (!the_index.initialized)
                if (read_cache() < 0)
                        die("index file corrupt");
  
-       /* 4: "fetch" (options) "--submodule-prefix" prefix NULL */
-       argv = xcalloc(num_options + 4, sizeof(const char *));
+       /* 6: "fetch" (options) --recurse-submodules-default default "--submodule-prefix" prefix NULL */
+       argv = xcalloc(num_options + 6, sizeof(const char *));
        argv[argc++] = "fetch";
        for (i = 0; i < num_options; i++)
                argv[argc++] = options[i];
+       argv[argc++] = "--recurse-submodules-default";
+       default_argc = argc++;
        argv[argc++] = "--submodule-prefix";
  
        memset(&cp, 0, sizeof(cp));
                struct strbuf submodule_git_dir = STRBUF_INIT;
                struct strbuf submodule_prefix = STRBUF_INIT;
                struct cache_entry *ce = active_cache[i];
-               const char *git_dir, *name;
+               const char *git_dir, *name, *default_argv;
  
                if (!S_ISGITLINK(ce->ce_mode))
                        continue;
                if (name_for_path)
                        name = name_for_path->util;
  
-               if (!ignore_config) {
+               default_argv = "yes";
+               if (command_line_option == RECURSE_SUBMODULES_DEFAULT) {
                        struct string_list_item *fetch_recurse_submodules_option;
                        fetch_recurse_submodules_option = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name);
                        if (fetch_recurse_submodules_option) {
-                               if (!fetch_recurse_submodules_option->util)
+                               if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_OFF)
                                        continue;
+                               if ((intptr_t)fetch_recurse_submodules_option->util == RECURSE_SUBMODULES_ON_DEMAND) {
+                                       if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
+                                               continue;
+                                       default_argv = "on-demand";
+                               }
                        } else {
-                               if (!config_fetch_recurse_submodules)
+                               if (config_fetch_recurse_submodules == RECURSE_SUBMODULES_OFF)
                                        continue;
+                               if (config_fetch_recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) {
+                                       if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
+                                               continue;
+                                       default_argv = "on-demand";
+                               }
                        }
+               } else if (command_line_option == RECURSE_SUBMODULES_ON_DEMAND) {
+                       if (!unsorted_string_list_lookup(&changed_submodule_paths, ce->name))
+                               continue;
+                       default_argv = "on-demand";
                }
  
                strbuf_addf(&submodule_path, "%s/%s", work_tree, ce->name);
                        if (!quiet)
                                printf("Fetching submodule %s%s\n", prefix, ce->name);
                        cp.dir = submodule_path.buf;
+                       argv[default_argc] = default_argv;
                        argv[argc] = submodule_prefix.buf;
                        if (run_command(&cp))
                                result = 1;
                strbuf_release(&submodule_prefix);
        }
        free(argv);
+ out:
+       string_list_clear(&changed_submodule_paths, 1);
        return result;
  }
  
index 8f1237987944b6b7e59b2b24ed785daf3cbba040,3decfae6e0957c0f2e2499ec06ff6ab4227da8b9..af78e21ba913b465e7bf2e4149549d32de9d4240
@@@ -66,10 -66,7 +66,10 @@@ test_expect_success "fetch --recurse-su
        (
                cd downstream &&
                git fetch --recurse-submodules >../actual.out 2>../actual.err
 -      ) &&
 +      )
 +'
 +
 +test_expect_success C_LOCALE_OUTPUT "fetch --recurse-submodules recurses into submodules: output" '
        test_cmp expect.out actual.out &&
        test_cmp expect.err actual.err
  '
@@@ -98,10 -95,7 +98,10 @@@ test_expect_success "using fetchRecurse
                cd downstream &&
                git config -f .gitmodules submodule.submodule.fetchRecurseSubmodules true &&
                git fetch >../actual.out 2>../actual.err
 -      ) &&
 +      )
 +'
 +
 +test_expect_success C_LOCALE_OUTPUT "using fetchRecurseSubmodules=true in .gitmodules recurses into submodules" '
        test_cmp expect.out actual.out &&
        test_cmp expect.err actual.err
  '
@@@ -132,10 -126,7 +132,10 @@@ test_expect_success "--recurse-submodul
                git fetch --recurse-submodules >../actual.out 2>../actual.err &&
                git config --unset -f .gitmodules submodule.submodule.fetchRecurseSubmodules &&
                git config --unset submodule.submodule.fetchRecurseSubmodules
 -      ) &&
 +      )
 +'
 +
 +test_expect_success C_LOCALE_OUTPUT "--recurse-submodules overrides fetchRecurseSubmodules setting from .git/config: output" '
        test_cmp expect.out actual.out &&
        test_cmp expect.err actual.err
  '
@@@ -154,22 -145,13 +154,22 @@@ test_expect_success "--dry-run propagat
        (
                cd downstream &&
                git fetch --recurse-submodules --dry-run >../actual.out 2>../actual.err
 -      ) &&
 +      )
 +'
 +
 +test_expect_success C_LOCALE_OUTPUT "--dry-run propagates to submodules: output" '
        test_cmp expect.out actual.out &&
 -      test_cmp expect.err actual.err &&
 +      test_cmp expect.err actual.err
 +'
 +
 +test_expect_success "Without --dry-run propagates to submodules" '
        (
                cd downstream &&
                git fetch --recurse-submodules >../actual.out 2>../actual.err
 -      ) &&
 +      )
 +'
 +
 +test_expect_success C_LOCALE_OUTPUT "Without --dry-run propagates to submodules: output" '
        test_cmp expect.out actual.out &&
        test_cmp expect.err actual.err
  '
@@@ -180,10 -162,7 +180,10 @@@ test_expect_success "recurseSubmodules=
                cd downstream &&
                git config fetch.recurseSubmodules true
                git fetch >../actual.out 2>../actual.err
 -      ) &&
 +      )
 +'
 +
 +test_expect_success C_LOCALE_OUTPUT "recurseSubmodules=true propagates into submodules: output" '
        test_cmp expect.out actual.out &&
        test_cmp expect.err actual.err
  '
@@@ -197,10 -176,7 +197,10 @@@ test_expect_success "--recurse-submodul
                        git config fetch.recurseSubmodules false
                ) &&
                git fetch --recurse-submodules >../actual.out 2>../actual.err
 -      ) &&
 +      )
 +'
 +
 +test_expect_success C_LOCALE_OUTPUT "--recurse-submodules overrides config in submodule: output" '
        test_cmp expect.out actual.out &&
        test_cmp expect.err actual.err
  '
@@@ -216,4 -192,259 +216,259 @@@ test_expect_success "--no-recurse-submo
        ! test -s actual.err
  '
  
+ test_expect_success "Recursion doesn't happen when no new commits are fetched in the superproject" '
+       (
+               cd downstream &&
+               (
+                       cd submodule &&
+                       git config --unset fetch.recurseSubmodules
+               ) &&
+               git config --unset fetch.recurseSubmodules
+               git fetch >../actual.out 2>../actual.err
+       ) &&
+       ! test -s actual.out &&
+       ! test -s actual.err
+ '
+ test_expect_success "Recursion stops when no new submodule commits are fetched" '
+       head1=$(git rev-parse --short HEAD) &&
+       git add submodule &&
+       git commit -m "new submodule" &&
+       head2=$(git rev-parse --short HEAD) &&
+       echo "Fetching submodule submodule" > expect.out.sub &&
+       echo "From $pwd/." > expect.err.sub &&
+       echo "   $head1..$head2  master     -> origin/master" >> expect.err.sub
+       head -2 expect.err >> expect.err.sub &&
+       (
+               cd downstream &&
+               git fetch >../actual.out 2>../actual.err
+       ) &&
+       test_cmp expect.err.sub actual.err &&
+       test_cmp expect.out.sub actual.out
+ '
+ test_expect_success "Recursion doesn't happen when new superproject commits don't change any submodules" '
+       add_upstream_commit &&
+       head1=$(git rev-parse --short HEAD) &&
+       echo a > file &&
+       git add file &&
+       git commit -m "new file" &&
+       head2=$(git rev-parse --short HEAD) &&
+       echo "From $pwd/." > expect.err.file &&
+       echo "   $head1..$head2  master     -> origin/master" >> expect.err.file &&
+       (
+               cd downstream &&
+               git fetch >../actual.out 2>../actual.err
+       ) &&
+       ! test -s actual.out &&
+       test_cmp expect.err.file actual.err
+ '
+ test_expect_success "Recursion picks up config in submodule" '
+       (
+               cd downstream &&
+               git fetch --recurse-submodules &&
+               (
+                       cd submodule &&
+                       git config fetch.recurseSubmodules true
+               )
+       ) &&
+       add_upstream_commit &&
+       head1=$(git rev-parse --short HEAD) &&
+       git add submodule &&
+       git commit -m "new submodule" &&
+       head2=$(git rev-parse --short HEAD) &&
+       echo "From $pwd/." > expect.err.sub &&
+       echo "   $head1..$head2  master     -> origin/master" >> expect.err.sub &&
+       cat expect.err >> expect.err.sub &&
+       (
+               cd downstream &&
+               git fetch >../actual.out 2>../actual.err &&
+               (
+                       cd submodule &&
+                       git config --unset fetch.recurseSubmodules
+               )
+       ) &&
+       test_cmp expect.err.sub actual.err &&
+       test_cmp expect.out actual.out
+ '
+ test_expect_success "Recursion picks up all submodules when necessary" '
+       add_upstream_commit &&
+       (
+               cd submodule &&
+               (
+                       cd deepsubmodule &&
+                       git fetch &&
+                       git checkout -q FETCH_HEAD
+               ) &&
+               head1=$(git rev-parse --short HEAD^) &&
+               git add deepsubmodule &&
+               git commit -m "new deepsubmodule"
+               head2=$(git rev-parse --short HEAD) &&
+               echo "From $pwd/submodule" > ../expect.err.sub &&
+               echo "   $head1..$head2  master     -> origin/master" >> ../expect.err.sub
+       ) &&
+       head1=$(git rev-parse --short HEAD) &&
+       git add submodule &&
+       git commit -m "new submodule" &&
+       head2=$(git rev-parse --short HEAD) &&
+       echo "From $pwd/." > expect.err.2 &&
+       echo "   $head1..$head2  master     -> origin/master" >> expect.err.2 &&
+       cat expect.err.sub >> expect.err.2 &&
+       tail -2 expect.err >> expect.err.2 &&
+       (
+               cd downstream &&
+               git fetch >../actual.out 2>../actual.err
+       ) &&
+       test_cmp expect.err.2 actual.err &&
+       test_cmp expect.out actual.out
+ '
+ test_expect_success "'--recurse-submodules=on-demand' doesn't recurse when no new commits are fetched in the superproject (and ignores config)" '
+       add_upstream_commit &&
+       (
+               cd submodule &&
+               (
+                       cd deepsubmodule &&
+                       git fetch &&
+                       git checkout -q FETCH_HEAD
+               ) &&
+               head1=$(git rev-parse --short HEAD^) &&
+               git add deepsubmodule &&
+               git commit -m "new deepsubmodule"
+               head2=$(git rev-parse --short HEAD) &&
+               echo "From $pwd/submodule" > ../expect.err.sub &&
+               echo "   $head1..$head2  master     -> origin/master" >> ../expect.err.sub
+       ) &&
+       (
+               cd downstream &&
+               git config fetch.recurseSubmodules true &&
+               git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err &&
+               git config --unset fetch.recurseSubmodules
+       ) &&
+       ! test -s actual.out &&
+       ! test -s actual.err
+ '
+ test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necessary (and ignores config)" '
+       head1=$(git rev-parse --short HEAD) &&
+       git add submodule &&
+       git commit -m "new submodule" &&
+       head2=$(git rev-parse --short HEAD) &&
+       tail -2 expect.err > expect.err.deepsub &&
+       echo "From $pwd/." > expect.err &&
+       echo "   $head1..$head2  master     -> origin/master" >> expect.err
+       cat expect.err.sub >> expect.err &&
+       cat expect.err.deepsub >> expect.err &&
+       (
+               cd downstream &&
+               git config fetch.recurseSubmodules false &&
+               (
+                       cd submodule &&
+                       git config -f .gitmodules submodule.deepsubmodule.fetchRecursive false
+               ) &&
+               git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err &&
+               git config --unset fetch.recurseSubmodules
+               (
+                       cd submodule &&
+                       git config --unset -f .gitmodules submodule.deepsubmodule.fetchRecursive
+               )
+       ) &&
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
+ '
+ test_expect_success "'--recurse-submodules=on-demand' stops when no new submodule commits are found in the superproject (and ignores config)" '
+       add_upstream_commit &&
+       head1=$(git rev-parse --short HEAD) &&
+       echo a >> file &&
+       git add file &&
+       git commit -m "new file" &&
+       head2=$(git rev-parse --short HEAD) &&
+       echo "From $pwd/." > expect.err.file &&
+       echo "   $head1..$head2  master     -> origin/master" >> expect.err.file &&
+       (
+               cd downstream &&
+               git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err
+       ) &&
+       ! test -s actual.out &&
+       test_cmp expect.err.file actual.err
+ '
+ test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config" '
+       (
+               cd downstream &&
+               git fetch --recurse-submodules
+       ) &&
+       add_upstream_commit &&
+       git config --global fetch.recurseSubmodules false &&
+       head1=$(git rev-parse --short HEAD) &&
+       git add submodule &&
+       git commit -m "new submodule" &&
+       head2=$(git rev-parse --short HEAD) &&
+       echo "From $pwd/." > expect.err.2 &&
+       echo "   $head1..$head2  master     -> origin/master" >> expect.err.2
+       head -2 expect.err >> expect.err.2 &&
+       (
+               cd downstream &&
+               git config fetch.recurseSubmodules on-demand &&
+               git fetch >../actual.out 2>../actual.err
+       ) &&
+       git config --global --unset fetch.recurseSubmodules &&
+       (
+               cd downstream &&
+               git config --unset fetch.recurseSubmodules
+       ) &&
+       test_cmp expect.out.sub actual.out &&
+       test_cmp expect.err.2 actual.err
+ '
+ test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules" '
+       (
+               cd downstream &&
+               git fetch --recurse-submodules
+       ) &&
+       add_upstream_commit &&
+       git config fetch.recurseSubmodules false &&
+       head1=$(git rev-parse --short HEAD) &&
+       git add submodule &&
+       git commit -m "new submodule" &&
+       head2=$(git rev-parse --short HEAD) &&
+       echo "From $pwd/." > expect.err.2 &&
+       echo "   $head1..$head2  master     -> origin/master" >> expect.err.2
+       head -2 expect.err >> expect.err.2 &&
+       (
+               cd downstream &&
+               git config submodule.submodule.fetchRecurseSubmodules on-demand &&
+               git fetch >../actual.out 2>../actual.err
+       ) &&
+       git config --unset fetch.recurseSubmodules &&
+       (
+               cd downstream &&
+               git config --unset submodule.submodule.fetchRecurseSubmodules
+       ) &&
+       test_cmp expect.out.sub actual.out &&
+       test_cmp expect.err.2 actual.err
+ '
+ test_expect_success "don't fetch submodule when newly recorded commits are already present" '
+       (
+               cd submodule &&
+               git checkout -q HEAD^^
+       ) &&
+       head1=$(git rev-parse --short HEAD) &&
+       git add submodule &&
+       git commit -m "submodule rewound" &&
+       head2=$(git rev-parse --short HEAD) &&
+       echo "From $pwd/." > expect.err &&
+       echo "   $head1..$head2  master     -> origin/master" >> expect.err &&
+       (
+               cd downstream &&
+               git fetch >../actual.out 2>../actual.err
+       ) &&
+       ! test -s actual.out &&
+       test_cmp expect.err actual.err
+ '
  test_done
index fa9d23aa31302f53cc1c39473492cf545ca5ae87,ee3eec51571cd64c815924269d475b4a392cb361..bf7c788735d6e3e0ecf56e7a2f82826701ee1789
@@@ -74,6 -74,26 +74,26 @@@ test_expect_success 'submodule update d
        )
  '
  
+ apos="'";
+ test_expect_success 'submodule update does not fetch already present commits' '
+       (cd submodule &&
+         echo line3 >> file &&
+         git add file &&
+         test_tick &&
+         git commit -m "upstream line3"
+       ) &&
+       (cd super/submodule &&
+         head=$(git rev-parse --verify HEAD) &&
+         echo "Submodule path ${apos}submodule$apos: checked out $apos$head$apos" > ../../expected &&
+         git reset --hard HEAD~1
+       ) &&
+       (cd super &&
+         git submodule update > ../actual 2> ../actual.err
+       ) &&
+       test_cmp expected actual &&
+       ! test -s actual.err
+ '
  test_expect_success 'submodule update --rebase staying on master' '
        (cd super/submodule &&
          git checkout master
@@@ -203,56 -223,4 +223,56 @@@ test_expect_success 'submodule init pic
        )
  '
  
 +test_expect_success 'submodule update --merge  - ignores --merge  for new submodules' '
 +      (cd super &&
 +       rm -rf submodule &&
 +       git submodule update submodule &&
 +       git status -s submodule >expect &&
 +       rm -rf submodule &&
 +       git submodule update --merge submodule &&
 +       git status -s submodule >actual &&
 +       test_cmp expect actual
 +      )
 +'
 +
 +test_expect_success 'submodule update --rebase - ignores --rebase for new submodules' '
 +      (cd super &&
 +       rm -rf submodule &&
 +       git submodule update submodule &&
 +       git status -s submodule >expect &&
 +       rm -rf submodule &&
 +       git submodule update --rebase submodule &&
 +       git status -s submodule >actual &&
 +       test_cmp expect actual
 +      )
 +'
 +
 +test_expect_success 'submodule update ignores update=merge config for new submodules' '
 +      (cd super &&
 +       rm -rf submodule &&
 +       git submodule update submodule &&
 +       git status -s submodule >expect &&
 +       rm -rf submodule &&
 +       git config submodule.submodule.update merge &&
 +       git submodule update submodule &&
 +       git status -s submodule >actual &&
 +       git config --unset submodule.submodule.update &&
 +       test_cmp expect actual
 +      )
 +'
 +
 +test_expect_success 'submodule update ignores update=rebase config for new submodules' '
 +      (cd super &&
 +       rm -rf submodule &&
 +       git submodule update submodule &&
 +       git status -s submodule >expect &&
 +       rm -rf submodule &&
 +       git config submodule.submodule.update rebase &&
 +       git submodule update submodule &&
 +       git status -s submodule >actual &&
 +       git config --unset submodule.submodule.update &&
 +       test_cmp expect actual
 +      )
 +'
 +
  test_done