sub-process: move sub-process functions into separate files
[gitweb.git] / submodule.c
index 3200b7bb2b2360c7efab86b680ed5d0cafa5e9d3..040f4c2287190cca706f9cd007c5942ae5c2b117 100644 (file)
@@ -17,6 +17,7 @@
 #include "worktree.h"
 
 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 int initialized_fetch_ref_tips;
@@ -234,15 +235,12 @@ int is_submodule_initialized(const char *path)
        return ret;
 }
 
-/*
- * Determine if a submodule has been populated at a given 'path'
- */
-int is_submodule_populated(const char *path)
+int is_submodule_populated_gently(const char *path, int *return_error_code)
 {
        int ret = 0;
        char *gitdir = xstrfmt("%s/.git", path);
 
-       if (resolve_gitdir(gitdir))
+       if (resolve_gitdir_gently(gitdir, return_error_code))
                ret = 1;
 
        free(gitdir);
@@ -358,6 +356,23 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
        strbuf_release(&sb);
 }
 
+static void prepare_submodule_repo_env_no_git_dir(struct argv_array *out)
+{
+       const char * const *var;
+
+       for (var = local_repo_env; *var; var++) {
+               if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
+                       argv_array_push(out, *var);
+       }
+}
+
+void prepare_submodule_repo_env(struct argv_array *out)
+{
+       prepare_submodule_repo_env_no_git_dir(out);
+       argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
+                        DEFAULT_GIT_DIR_ENVIRONMENT);
+}
+
 /* Helper function to display the submodule header line prior to the full
  * summary output. If it can locate the submodule objects directory it will
  * attempt to lookup both the left and right commits and put them into the
@@ -545,6 +560,27 @@ void set_config_fetch_recurse_submodules(int value)
        config_fetch_recurse_submodules = value;
 }
 
+void set_config_update_recurse_submodules(int value)
+{
+       config_update_recurse_submodules = value;
+}
+
+int should_update_submodules(void)
+{
+       return config_update_recurse_submodules == RECURSE_SUBMODULES_ON;
+}
+
+const struct submodule *submodule_from_ce(const struct cache_entry *ce)
+{
+       if (!S_ISGITLINK(ce->ce_mode))
+               return NULL;
+
+       if (!should_update_submodules())
+               return NULL;
+
+       return submodule_from_path(null_sha1, ce->name);
+}
+
 static int has_remote(const char *refname, const struct object_id *oid,
                      int flags, void *cb_data)
 {
@@ -1203,6 +1239,151 @@ int bad_to_remove_submodule(const char *path, unsigned flags)
        return ret;
 }
 
+static const char *get_super_prefix_or_empty(void)
+{
+       const char *s = get_super_prefix();
+       if (!s)
+               s = "";
+       return s;
+}
+
+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);
+
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "diff-index", "--quiet",
+                                  "--cached", "HEAD", NULL);
+       cp.no_stdin = 1;
+       cp.no_stdout = 1;
+       cp.dir = sub->path;
+       if (start_command(&cp))
+               die("could not recurse into submodule '%s'", sub->path);
+
+       return finish_command(&cp);
+}
+
+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);
+
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       cp.dir = path;
+
+       argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
+                                  get_super_prefix_or_empty(), path);
+       argv_array_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
+
+       argv_array_push(&cp.args, EMPTY_TREE_SHA1_HEX);
+
+       if (run_command(&cp))
+               die("could not reset submodule index");
+}
+
+/**
+ * Moves a submodule at a given path from a given head to another new head.
+ * For edge cases (a submodule coming into existence or removing a submodule)
+ * pass NULL for old or new respectively.
+ */
+int submodule_move_head(const char *path,
+                        const char *old,
+                        const char *new,
+                        unsigned flags)
+{
+       int ret = 0;
+       struct child_process cp = CHILD_PROCESS_INIT;
+       const struct submodule *sub;
+
+       sub = submodule_from_path(null_sha1, path);
+
+       if (!sub)
+               die("BUG: could not get submodule information for '%s'", path);
+
+       if (old && !(flags & SUBMODULE_MOVE_HEAD_FORCE)) {
+               /* Check if the submodule has a dirty index. */
+               if (submodule_has_dirty_index(sub))
+                       return error(_("submodule '%s' has dirty index"), path);
+       }
+
+       if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
+               if (old) {
+                       if (!submodule_uses_gitfile(path))
+                               absorb_git_dir_into_superproject("", path,
+                                       ABSORB_GITDIR_RECURSE_SUBMODULES);
+               } else {
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addf(&sb, "%s/modules/%s",
+                                   get_git_common_dir(), sub->name);
+                       connect_work_tree_and_git_dir(path, sb.buf);
+                       strbuf_release(&sb);
+
+                       /* make sure the index is clean as well */
+                       submodule_reset_index(path);
+               }
+       }
+
+       prepare_submodule_repo_env_no_git_dir(&cp.env_array);
+
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       cp.dir = path;
+
+       argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
+                       get_super_prefix_or_empty(), path);
+       argv_array_pushl(&cp.args, "read-tree", NULL);
+
+       if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
+               argv_array_push(&cp.args, "-n");
+       else
+               argv_array_push(&cp.args, "-u");
+
+       if (flags & SUBMODULE_MOVE_HEAD_FORCE)
+               argv_array_push(&cp.args, "--reset");
+       else
+               argv_array_push(&cp.args, "-m");
+
+       argv_array_push(&cp.args, old ? old : EMPTY_TREE_SHA1_HEX);
+       argv_array_push(&cp.args, new ? new : EMPTY_TREE_SHA1_HEX);
+
+       if (run_command(&cp)) {
+               ret = -1;
+               goto out;
+       }
+
+       if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
+               if (new) {
+                       struct child_process cp1 = CHILD_PROCESS_INIT;
+                       /* also set the HEAD accordingly */
+                       cp1.git_cmd = 1;
+                       cp1.no_stdin = 1;
+                       cp1.dir = path;
+
+                       argv_array_pushl(&cp1.args, "update-ref", "HEAD",
+                                        new ? new : EMPTY_TREE_SHA1_HEX, NULL);
+
+                       if (run_command(&cp1)) {
+                               ret = -1;
+                               goto out;
+                       }
+               } else {
+                       struct strbuf sb = STRBUF_INIT;
+
+                       strbuf_addf(&sb, "%s/.git", path);
+                       unlink_or_warn(sb.buf);
+                       strbuf_release(&sb);
+
+                       if (is_empty_dir(path))
+                               rmdir_or_warn(path);
+               }
+       }
+out:
+       return ret;
+}
+
 static int find_first_merges(struct object_array *result, const char *path,
                struct commit *a, struct commit *b)
 {
@@ -1371,18 +1552,6 @@ int parallel_submodules(void)
        return parallel_jobs;
 }
 
-void prepare_submodule_repo_env(struct argv_array *out)
-{
-       const char * const *var;
-
-       for (var = local_repo_env; *var; var++) {
-               if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
-                       argv_array_push(out, *var);
-       }
-       argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
-                        DEFAULT_GIT_DIR_ENVIRONMENT);
-}
-
 /*
  * Embeds a single submodules git directory into the superprojects git dir,
  * non recursively.
@@ -1414,11 +1583,8 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
                die(_("could not create directory '%s'"), new_git_dir);
        real_new_git_dir = real_pathdup(new_git_dir, 1);
 
-       if (!prefix)
-               prefix = get_super_prefix();
-
        fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
-               prefix ? prefix : "", path,
+               get_super_prefix_or_empty(), path,
                real_old_git_dir, real_new_git_dir);
 
        relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
@@ -1445,8 +1611,6 @@ void absorb_git_dir_into_superproject(const char *prefix,
 
        /* Not populated? */
        if (!sub_git_dir) {
-               char *real_new_git_dir;
-               const char *new_git_dir;
                const struct submodule *sub;
 
                if (err_code == READ_GITFILE_ERR_STAT_FAILED) {
@@ -1469,13 +1633,8 @@ void absorb_git_dir_into_superproject(const char *prefix,
                sub = submodule_from_path(null_sha1, path);
                if (!sub)
                        die(_("could not lookup name for submodule '%s'"), path);
-               new_git_dir = git_path("modules/%s", sub->name);
-               if (safe_create_leading_directories_const(new_git_dir) < 0)
-                       die(_("could not create directory '%s'"), new_git_dir);
-               real_new_git_dir = real_pathdup(new_git_dir, 1);
-               connect_work_tree_and_git_dir(path, real_new_git_dir);
-
-               free(real_new_git_dir);
+               connect_work_tree_and_git_dir(path,
+                       git_path("modules/%s", sub->name));
        } else {
                /* Is it already absorbed into the superprojects git dir? */
                char *real_sub_git_dir = real_pathdup(sub_git_dir, 1);
@@ -1496,8 +1655,7 @@ void absorb_git_dir_into_superproject(const char *prefix,
                if (flags & ~ABSORB_GITDIR_RECURSE_SUBMODULES)
                        die("BUG: we don't know how to pass the flags down?");
 
-               if (get_super_prefix())
-                       strbuf_addstr(&sb, get_super_prefix());
+               strbuf_addstr(&sb, get_super_prefix_or_empty());
                strbuf_addstr(&sb, path);
                strbuf_addch(&sb, '/');