]> git.ipfire.org Git - thirdparty/git.git/commitdiff
fetch/pull: Don't recurse into a submodule when commits are already present
authorJens Lehmann <Jens.Lehmann@web.de>
Sun, 6 Mar 2011 22:12:58 +0000 (23:12 +0100)
committerJunio C Hamano <gitster@pobox.com>
Wed, 9 Mar 2011 21:10:35 +0000 (13:10 -0800)
When looking for submodules where new commits have been recorded in the
superproject ignore those cases where the submodules commits are already
present locally. This can happen e.g. when the submodule has been rewound
to an earlier state. Then there is no need to fetch the submodule again
as the commit recorded in the newly fetched superproject commit has
already been fetched earlier into the submodule.

Signed-off-by: Jens Lehmann <Jens.Lehmann@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/fetch-options.txt
submodule.c
t/t5526-fetch-submodules.sh

index d2870280c7d4f3931428084d7e0047b149054c90..39d326abc63af01d7cffe3d6f0c3df9279bc667e 100644 (file)
@@ -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
index 924b156950982db56ab6ddf1093aff30375e5ee4..88c7488a63bf2a3280cdc536609a1d2b15541d0c 100644 (file)
@@ -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 */
index 09701aa1a71fa51b54fd2bf68858437eb63c1ee2..3decfae6e0957c0f2e2499ec06ff6ab4227da8b9 100755 (executable)
@@ -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