diff options
-rw-r--r-- | Documentation/fetch-options.txt | 3 | ||||
-rw-r--r-- | submodule.c | 29 | ||||
-rwxr-xr-x | t/t5526-fetch-submodules.sh | 19 |
3 files changed, 49 insertions, 2 deletions
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt index d2870280c7..39d326abc6 100644 --- a/Documentation/fetch-options.txt +++ b/Documentation/fetch-options.txt @@ -73,7 +73,8 @@ ifndef::git-pull[] 'yes', which is the default when this option is used without any value. Use 'on-demand' to only recurse into a populated submodule when the superproject retrieves a commit that updates the submodule's - reference. + reference to a commit that isn't already in the local submodule + clone. --no-recurse-submodules:: Disable recursive fetching of submodules (this has the same effect as diff --git a/submodule.c b/submodule.c index 924b156950..88c7488a63 100644 --- a/submodule.c +++ b/submodule.c @@ -263,6 +263,33 @@ void set_config_fetch_recurse_submodules(int value) 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) @@ -280,7 +307,7 @@ static void submodule_collect_changed_cb(struct diff_queue_struct *q, * being moved around. */ struct string_list_item *path; path = unsorted_string_list_lookup(&changed_submodule_paths, p->two->path); - if (!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 */ diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh index 09701aa1a7..3decfae6e0 100755 --- a/t/t5526-fetch-submodules.sh +++ b/t/t5526-fetch-submodules.sh @@ -428,4 +428,23 @@ test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' override 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 |