Merge branch 'jk/alternate-ref-optim'
[gitweb.git] / submodule.c
index 057695e645342bedef3195f14e33a7a708baae69..9d5ec5f08454c5e1ef1dc4295795e7915c0959bf 100644 (file)
@@ -447,8 +447,8 @@ static void show_submodule_header(FILE *f, const char *path,
         * Attempt to lookup the commit references, and determine if this is
         * a fast forward or fast backwards update.
         */
-       *left = lookup_commit_reference(one->hash);
-       *right = lookup_commit_reference(two->hash);
+       *left = lookup_commit_reference(one);
+       *right = lookup_commit_reference(two);
 
        /*
         * Warn about missing commits in the submodule project, but only if
@@ -554,7 +554,8 @@ void show_submodule_inline_diff(FILE *f, const char *path,
        cp.no_stdin = 1;
 
        /* TODO: other options may need to be passed here. */
-       argv_array_push(&cp.args, "diff");
+       argv_array_pushl(&cp.args, "diff", "--submodule=diff", NULL);
+
        argv_array_pushf(&cp.args, "--line-prefix=%s", line_prefix);
        if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
                argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
@@ -617,6 +618,94 @@ const struct submodule *submodule_from_ce(const struct cache_entry *ce)
        return submodule_from_path(null_sha1, ce->name);
 }
 
+static struct oid_array *submodule_commits(struct string_list *submodules,
+                                          const char *path)
+{
+       struct string_list_item *item;
+
+       item = string_list_insert(submodules, path);
+       if (item->util)
+               return (struct oid_array *) item->util;
+
+       /* NEEDSWORK: should we have oid_array_init()? */
+       item->util = xcalloc(1, sizeof(struct oid_array));
+       return (struct oid_array *) item->util;
+}
+
+static void collect_changed_submodules_cb(struct diff_queue_struct *q,
+                                         struct diff_options *options,
+                                         void *data)
+{
+       int i;
+       struct string_list *changed = data;
+
+       for (i = 0; i < q->nr; i++) {
+               struct diff_filepair *p = q->queue[i];
+               struct oid_array *commits;
+               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.
+                        */
+                       commits = submodule_commits(changed, p->two->path);
+                       oid_array_append(commits, &p->two->oid);
+               } 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;
+               }
+       }
+}
+
+/*
+ * Collect the paths of submodules in 'changed' which have changed based on
+ * the revisions as specified in 'argv'.  Each entry in 'changed' will also
+ * have a corresponding 'struct oid_array' (in the 'util' field) which lists
+ * what the submodule pointers were updated to during the change.
+ */
+static void collect_changed_submodules(struct string_list *changed,
+                                      struct argv_array *argv)
+{
+       struct rev_info rev;
+       const struct commit *commit;
+
+       init_revisions(&rev, NULL);
+       setup_revisions(argv->argc, argv->argv, &rev, NULL);
+       if (prepare_revision_walk(&rev))
+               die("revision walk setup failed");
+
+       while ((commit = get_revision(&rev))) {
+               struct rev_info diff_rev;
+
+               init_revisions(&diff_rev, NULL);
+               diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
+               diff_rev.diffopt.format_callback = collect_changed_submodules_cb;
+               diff_rev.diffopt.format_callback_data = changed;
+               diff_tree_combined_merge(commit, 1, &diff_rev);
+       }
+
+       reset_revision_walk();
+}
+
+static void free_submodules_oids(struct string_list *submodules)
+{
+       struct string_list_item *item;
+       for_each_string_list_item(item, submodules)
+               oid_array_clear((struct oid_array *) item->util);
+       string_list_clear(submodules, 1);
+}
+
 static int has_remote(const char *refname, const struct object_id *oid,
                      int flags, void *cb_data)
 {
@@ -634,7 +723,7 @@ static int check_has_commit(const struct object_id *oid, void *data)
 {
        int *has_commit = data;
 
-       if (!lookup_commit_reference(oid->hash))
+       if (!lookup_commit_reference(oid))
                *has_commit = 0;
 
        return 0;
@@ -729,92 +818,31 @@ static int submodule_needs_pushing(const char *path, struct oid_array *commits)
        return 0;
 }
 
-static struct oid_array *submodule_commits(struct string_list *submodules,
-                                           const char *path)
-{
-       struct string_list_item *item;
-
-       item = string_list_insert(submodules, path);
-       if (item->util)
-               return (struct oid_array *) item->util;
-
-       /* NEEDSWORK: should we have oid_array_init()? */
-       item->util = xcalloc(1, sizeof(struct oid_array));
-       return (struct oid_array *) item->util;
-}
-
-static void collect_submodules_from_diff(struct diff_queue_struct *q,
-                                        struct diff_options *options,
-                                        void *data)
-{
-       int i;
-       struct string_list *submodules = data;
-
-       for (i = 0; i < q->nr; i++) {
-               struct diff_filepair *p = q->queue[i];
-               struct oid_array *commits;
-               if (!S_ISGITLINK(p->two->mode))
-                       continue;
-               commits = submodule_commits(submodules, p->two->path);
-               oid_array_append(commits, &p->two->oid);
-       }
-}
-
-static void find_unpushed_submodule_commits(struct commit *commit,
-               struct string_list *needs_pushing)
-{
-       struct rev_info rev;
-
-       init_revisions(&rev, NULL);
-       rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
-       rev.diffopt.format_callback = collect_submodules_from_diff;
-       rev.diffopt.format_callback_data = needs_pushing;
-       diff_tree_combined_merge(commit, 1, &rev);
-}
-
-static void free_submodules_oids(struct string_list *submodules)
-{
-       struct string_list_item *item;
-       for_each_string_list_item(item, submodules)
-               oid_array_clear((struct oid_array *) item->util);
-       string_list_clear(submodules, 1);
-}
-
 int find_unpushed_submodules(struct oid_array *commits,
                const char *remotes_name, struct string_list *needs_pushing)
 {
-       struct rev_info rev;
-       struct commit *commit;
        struct string_list submodules = STRING_LIST_INIT_DUP;
        struct string_list_item *submodule;
        struct argv_array argv = ARGV_ARRAY_INIT;
 
-       init_revisions(&rev, NULL);
-
        /* argv.argv[0] will be ignored by setup_revisions */
        argv_array_push(&argv, "find_unpushed_submodules");
        oid_array_for_each_unique(commits, append_oid_to_argv, &argv);
        argv_array_push(&argv, "--not");
        argv_array_pushf(&argv, "--remotes=%s", remotes_name);
 
-       setup_revisions(argv.argc, argv.argv, &rev, NULL);
-       if (prepare_revision_walk(&rev))
-               die("revision walk setup failed");
-
-       while ((commit = get_revision(&rev)) != NULL)
-               find_unpushed_submodule_commits(commit, &submodules);
-
-       reset_revision_walk();
-       argv_array_clear(&argv);
+       collect_changed_submodules(&submodules, &argv);
 
        for_each_string_list_item(submodule, &submodules) {
-               struct oid_array *commits = (struct oid_array *) submodule->util;
+               struct oid_array *commits = submodule->util;
+               const char *path = submodule->string;
 
-               if (submodule_needs_pushing(submodule->string, commits))
-                       string_list_insert(needs_pushing, submodule->string);
+               if (submodule_needs_pushing(path, commits))
+                       string_list_insert(needs_pushing, path);
        }
 
        free_submodules_oids(&submodules);
+       argv_array_clear(&argv);
 
        return needs_pushing->nr;
 }
@@ -931,61 +959,6 @@ int push_unpushed_submodules(struct oid_array *commits,
        return ret;
 }
 
-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 = CHILD_PROCESS_INIT;
-               const char *argv[] = {"rev-list", "-n", "1", NULL, "--not", "--all", NULL};
-               struct strbuf buf = STRBUF_INIT;
-
-               argv[3] = sha1_to_hex(sha1);
-               cp.argv = argv;
-               prepare_submodule_repo_env(&cp.env_array);
-               cp.git_cmd = 1;
-               cp.no_stdin = 1;
-               cp.dir = path;
-               if (!capture_command(&cp, &buf, 1024) && !buf.len)
-                       is_present = 1;
-
-               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->oid.hash))
-                               string_list_append(&changed_submodule_paths, 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;
-               }
-       }
-}
-
 static int append_oid_to_array(const char *ref, const struct object_id *oid,
                               int flags, void *data)
 {
@@ -1006,45 +979,36 @@ void check_for_new_submodule_commits(struct object_id *oid)
 
 static void calculate_changed_submodule_paths(void)
 {
-       struct rev_info rev;
-       struct commit *commit;
        struct argv_array argv = ARGV_ARRAY_INIT;
+       struct string_list changed_submodules = STRING_LIST_INIT_DUP;
+       const struct string_list_item *item;
 
        /* No need to check if there are no submodules configured */
        if (!submodule_from_path(NULL, NULL))
                return;
 
-       init_revisions(&rev, NULL);
        argv_array_push(&argv, "--"); /* argv[0] program name */
        oid_array_for_each_unique(&ref_tips_after_fetch,
                                   append_oid_to_argv, &argv);
        argv_array_push(&argv, "--not");
        oid_array_for_each_unique(&ref_tips_before_fetch,
                                   append_oid_to_argv, &argv);
-       setup_revisions(argv.argc, argv.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_OPT_SET(&diff_opts, RECURSIVE);
-                       diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
-                       diff_opts.format_callback = submodule_collect_changed_cb;
-                       diff_setup_done(&diff_opts);
-                       diff_tree_sha1(parent->item->object.oid.hash, commit->object.oid.hash, "", &diff_opts);
-                       diffcore_std(&diff_opts);
-                       diff_flush(&diff_opts);
-                       parent = parent->next;
-               }
+       collect_changed_submodules(&changed_submodules, &argv);
+
+       for_each_string_list_item(item, &changed_submodules) {
+               struct oid_array *commits = item->util;
+               const char *path = item->string;
+
+               if (!submodule_has_commits(path, commits))
+                       string_list_append(&changed_submodule_paths, path);
        }
 
+       free_submodules_oids(&changed_submodules);
        argv_array_clear(&argv);
        oid_array_clear(&ref_tips_before_fetch);
        oid_array_clear(&ref_tips_after_fetch);
@@ -1393,7 +1357,7 @@ 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);
+       prepare_submodule_repo_env(&cp.env_array);
 
        cp.git_cmd = 1;
        argv_array_pushl(&cp.args, "diff-index", "--quiet",
@@ -1410,7 +1374,7 @@ static int submodule_has_dirty_index(const struct submodule *sub)
 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);
+       prepare_submodule_repo_env(&cp.env_array);
 
        cp.git_cmd = 1;
        cp.no_stdin = 1;
@@ -1439,6 +1403,23 @@ int submodule_move_head(const char *path,
        int ret = 0;
        struct child_process cp = CHILD_PROCESS_INIT;
        const struct submodule *sub;
+       int *error_code_ptr, error_code;
+
+       if (!is_submodule_initialized(path))
+               return 0;
+
+       if (flags & SUBMODULE_MOVE_HEAD_FORCE)
+               /*
+                * Pass non NULL pointer to is_submodule_populated_gently
+                * to prevent die()-ing. We'll use connect_work_tree_and_git_dir
+                * to fixup the submodule in the force case later.
+                */
+               error_code_ptr = &error_code;
+       else
+               error_code_ptr = NULL;
+
+       if (old && !is_submodule_populated_gently(path, error_code_ptr))
+               return 0;
 
        sub = submodule_from_path(null_sha1, path);
 
@@ -1457,18 +1438,24 @@ int submodule_move_head(const char *path,
                                absorb_git_dir_into_superproject("", path,
                                        ABSORB_GITDIR_RECURSE_SUBMODULES);
                } else {
-                       struct strbuf sb = STRBUF_INIT;
-                       strbuf_addf(&sb, "%s/modules/%s",
+                       char *gitdir = xstrfmt("%s/modules/%s",
                                    get_git_common_dir(), sub->name);
-                       connect_work_tree_and_git_dir(path, sb.buf);
-                       strbuf_release(&sb);
+                       connect_work_tree_and_git_dir(path, gitdir);
+                       free(gitdir);
 
                        /* make sure the index is clean as well */
                        submodule_reset_index(path);
                }
+
+               if (old && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
+                       char *gitdir = xstrfmt("%s/modules/%s",
+                                   get_git_common_dir(), sub->name);
+                       connect_work_tree_and_git_dir(path, gitdir);
+                       free(gitdir);
+               }
        }
 
-       prepare_submodule_repo_env_no_git_dir(&cp.env_array);
+       prepare_submodule_repo_env(&cp.env_array);
 
        cp.git_cmd = 1;
        cp.no_stdin = 1;
@@ -1476,7 +1463,7 @@ int submodule_move_head(const char *path,
 
        argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
                        get_super_prefix_or_empty(), path);
-       argv_array_pushl(&cp.args, "read-tree", NULL);
+       argv_array_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL);
 
        if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
                argv_array_push(&cp.args, "-n");
@@ -1498,15 +1485,16 @@ int submodule_move_head(const char *path,
 
        if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
                if (new) {
-                       struct child_process cp1 = CHILD_PROCESS_INIT;
+                       child_process_init(&cp);
                        /* also set the HEAD accordingly */
-                       cp1.git_cmd = 1;
-                       cp1.no_stdin = 1;
-                       cp1.dir = path;
+                       cp.git_cmd = 1;
+                       cp.no_stdin = 1;
+                       cp.dir = path;
 
-                       argv_array_pushl(&cp1.args, "update-ref", "HEAD", new, NULL);
+                       prepare_submodule_repo_env(&cp.env_array);
+                       argv_array_pushl(&cp.args, "update-ref", "HEAD", new, NULL);
 
-                       if (run_command(&cp1)) {
+                       if (run_command(&cp)) {
                                ret = -1;
                                goto out;
                        }
@@ -1596,9 +1584,9 @@ static void print_commit(struct commit *commit)
 #define MERGE_WARNING(path, msg) \
        warning("Failed to merge submodule %s (%s)", path, msg);
 
-int merge_submodule(unsigned char result[20], const char *path,
-                   const unsigned char base[20], const unsigned char a[20],
-                   const unsigned char b[20], int search)
+int merge_submodule(struct object_id *result, const char *path,
+                   const struct object_id *base, const struct object_id *a,
+                   const struct object_id *b, int search)
 {
        struct commit *commit_base, *commit_a, *commit_b;
        int parent_count;
@@ -1607,14 +1595,14 @@ int merge_submodule(unsigned char result[20], const char *path,
        int i;
 
        /* store a in result in case we fail */
-       hashcpy(result, a);
+       oidcpy(result, a);
 
        /* we can not handle deletion conflicts */
-       if (is_null_sha1(base))
+       if (is_null_oid(base))
                return 0;
-       if (is_null_sha1(a))
+       if (is_null_oid(a))
                return 0;
-       if (is_null_sha1(b))
+       if (is_null_oid(b))
                return 0;
 
        if (add_submodule_odb(path)) {
@@ -1638,11 +1626,11 @@ int merge_submodule(unsigned char result[20], const char *path,
 
        /* Case #1: a is contained in b or vice versa */
        if (in_merge_bases(commit_a, commit_b)) {
-               hashcpy(result, b);
+               oidcpy(result, b);
                return 1;
        }
        if (in_merge_bases(commit_b, commit_a)) {
-               hashcpy(result, a);
+               oidcpy(result, a);
                return 1;
        }