]> git.ipfire.org Git - thirdparty/git.git/blobdiff - submodule.c
submodule: improve submodule_has_commits()
[thirdparty/git.git] / submodule.c
index 5615d7392efaf18816ba2a6910a224df581be72e..057695e645342bedef3195f14e33a7a708baae69 100644 (file)
@@ -20,7 +20,7 @@
 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 struct string_list changed_submodule_paths = STRING_LIST_INIT_DUP;
 static int initialized_fetch_ref_tips;
 static struct oid_array ref_tips_before_fetch;
 static struct oid_array ref_tips_after_fetch;
@@ -644,10 +644,44 @@ static int submodule_has_commits(const char *path, struct oid_array *commits)
 {
        int has_commit = 1;
 
+       /*
+        * Perform a cheap, but incorrect check for the existance of 'commits'.
+        * This is done by adding the submodule's object store to the in-core
+        * object store, and then querying for each commit's existance.  If we
+        * do not have the commit object anywhere, there is no chance we have
+        * it in the object store of the correct submodule and have it
+        * reachable from a ref, so we can fail early without spawning rev-list
+        * which is expensive.
+        */
        if (add_submodule_odb(path))
                return 0;
 
        oid_array_for_each_unique(commits, check_has_commit, &has_commit);
+
+       if (has_commit) {
+               /*
+                * Even if the submodule is checked out and the commit is
+                * present, make sure it exists in the submodule's object store
+                * and that it is reachable from a ref.
+                */
+               struct child_process cp = CHILD_PROCESS_INIT;
+               struct strbuf out = STRBUF_INIT;
+
+               argv_array_pushl(&cp.args, "rev-list", "-n", "1", NULL);
+               oid_array_for_each_unique(commits, append_oid_to_argv, &cp.args);
+               argv_array_pushl(&cp.args, "--not", "--all", NULL);
+
+               prepare_submodule_repo_env(&cp.env_array);
+               cp.git_cmd = 1;
+               cp.no_stdin = 1;
+               cp.dir = path;
+
+               if (capture_command(&cp, &out, GIT_MAX_HEXSZ + 1) || out.len)
+                       has_commit = 0;
+
+               strbuf_release(&out);
+       }
+
        return has_commit;
 }
 
@@ -738,7 +772,7 @@ static void find_unpushed_submodule_commits(struct commit *commit,
        diff_tree_combined_merge(commit, 1, &rev);
 }
 
-static void free_submodules_sha1s(struct string_list *submodules)
+static void free_submodules_oids(struct string_list *submodules)
 {
        struct string_list_item *item;
        for_each_string_list_item(item, submodules)
@@ -779,7 +813,8 @@ int find_unpushed_submodules(struct oid_array *commits,
                if (submodule_needs_pushing(submodule->string, commits))
                        string_list_insert(needs_pushing, submodule->string);
        }
-       free_submodules_sha1s(&submodules);
+
+       free_submodules_oids(&submodules);
 
        return needs_pushing->nr;
 }
@@ -938,7 +973,7 @@ static void submodule_collect_changed_cb(struct diff_queue_struct *q,
                        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->oid.hash))
-                               string_list_append(&changed_submodule_paths, xstrdup(p->two->path));
+                               string_list_append(&changed_submodule_paths, p->two->path);
                } else {
                        /* Submodule is new or was moved here */
                        /* NEEDSWORK: When the .git directories of submodules
@@ -951,29 +986,24 @@ static void submodule_collect_changed_cb(struct diff_queue_struct *q,
        }
 }
 
-static int add_sha1_to_array(const char *ref, const struct object_id *oid,
-                            int flags, void *data)
+static int append_oid_to_array(const char *ref, const struct object_id *oid,
+                              int flags, void *data)
 {
-       oid_array_append(data, oid);
+       struct oid_array *array = data;
+       oid_array_append(array, oid);
        return 0;
 }
 
 void check_for_new_submodule_commits(struct object_id *oid)
 {
        if (!initialized_fetch_ref_tips) {
-               for_each_ref(add_sha1_to_array, &ref_tips_before_fetch);
+               for_each_ref(append_oid_to_array, &ref_tips_before_fetch);
                initialized_fetch_ref_tips = 1;
        }
 
        oid_array_append(&ref_tips_after_fetch, oid);
 }
 
-static int add_oid_to_argv(const struct object_id *oid, void *data)
-{
-       argv_array_push(data, oid_to_hex(oid));
-       return 0;
-}
-
 static void calculate_changed_submodule_paths(void)
 {
        struct rev_info rev;
@@ -987,10 +1017,10 @@ static void calculate_changed_submodule_paths(void)
        init_revisions(&rev, NULL);
        argv_array_push(&argv, "--"); /* argv[0] program name */
        oid_array_for_each_unique(&ref_tips_after_fetch,
-                                  add_oid_to_argv, &argv);
+                                  append_oid_to_argv, &argv);
        argv_array_push(&argv, "--not");
        oid_array_for_each_unique(&ref_tips_before_fetch,
-                                  add_oid_to_argv, &argv);
+                                  append_oid_to_argv, &argv);
        setup_revisions(argv.argc, argv.argv, &rev, NULL);
        if (prepare_revision_walk(&rev))
                die("revision walk setup failed");
@@ -1474,8 +1504,7 @@ int submodule_move_head(const char *path,
                        cp1.no_stdin = 1;
                        cp1.dir = path;
 
-                       argv_array_pushl(&cp1.args, "update-ref", "HEAD",
-                                        new ? new : EMPTY_TREE_SHA1_HEX, NULL);
+                       argv_array_pushl(&cp1.args, "update-ref", "HEAD", new, NULL);
 
                        if (run_command(&cp1)) {
                                ret = -1;