bump to v2.0.1
[platform/upstream/git.git] / submodule.c
index 50f213e..b80ecac 100644 (file)
@@ -10,6 +10,7 @@
 #include "string-list.h"
 #include "sha1-array.h"
 #include "argv-array.h"
+#include "blob.h"
 
 static struct string_list config_name_for_path;
 static struct string_list config_fetch_recurse_submodules_for_name;
@@ -30,6 +31,95 @@ static struct sha1_array ref_tips_after_fetch;
  */
 static int gitmodules_is_unmerged;
 
+/*
+ * This flag is set if the .gitmodules file had unstaged modifications on
+ * startup. This must be checked before allowing modifications to the
+ * .gitmodules file with the intention to stage them later, because when
+ * continuing we would stage the modifications the user didn't stage herself
+ * too. That might change in a future version when we learn to stage the
+ * changes we do ourselves without staging any previous modifications.
+ */
+static int gitmodules_is_modified;
+
+
+int is_staging_gitmodules_ok(void)
+{
+       return !gitmodules_is_modified;
+}
+
+/*
+ * Try to update the "path" entry in the "submodule.<name>" section of the
+ * .gitmodules file. Return 0 only if a .gitmodules file was found, a section
+ * with the correct path=<oldpath> setting was found and we could update it.
+ */
+int update_path_in_gitmodules(const char *oldpath, const char *newpath)
+{
+       struct strbuf entry = STRBUF_INIT;
+       struct string_list_item *path_option;
+
+       if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
+               return -1;
+
+       if (gitmodules_is_unmerged)
+               die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
+
+       path_option = unsorted_string_list_lookup(&config_name_for_path, oldpath);
+       if (!path_option) {
+               warning(_("Could not find section in .gitmodules where path=%s"), oldpath);
+               return -1;
+       }
+       strbuf_addstr(&entry, "submodule.");
+       strbuf_addstr(&entry, path_option->util);
+       strbuf_addstr(&entry, ".path");
+       if (git_config_set_in_file(".gitmodules", entry.buf, newpath) < 0) {
+               /* Maybe the user already did that, don't error out here */
+               warning(_("Could not update .gitmodules entry %s"), entry.buf);
+               strbuf_release(&entry);
+               return -1;
+       }
+       strbuf_release(&entry);
+       return 0;
+}
+
+/*
+ * Try to remove the "submodule.<name>" section from .gitmodules where the given
+ * path is configured. Return 0 only if a .gitmodules file was found, a section
+ * with the correct path=<path> setting was found and we could remove it.
+ */
+int remove_path_from_gitmodules(const char *path)
+{
+       struct strbuf sect = STRBUF_INIT;
+       struct string_list_item *path_option;
+
+       if (!file_exists(".gitmodules")) /* Do nothing without .gitmodules */
+               return -1;
+
+       if (gitmodules_is_unmerged)
+               die(_("Cannot change unmerged .gitmodules, resolve merge conflicts first"));
+
+       path_option = unsorted_string_list_lookup(&config_name_for_path, path);
+       if (!path_option) {
+               warning(_("Could not find section in .gitmodules where path=%s"), path);
+               return -1;
+       }
+       strbuf_addstr(&sect, "submodule.");
+       strbuf_addstr(&sect, path_option->util);
+       if (git_config_rename_section_in_file(".gitmodules", sect.buf, NULL) < 0) {
+               /* Maybe the user already did that, don't error out here */
+               warning(_("Could not remove .gitmodules entry for %s"), path);
+               strbuf_release(&sect);
+               return -1;
+       }
+       strbuf_release(&sect);
+       return 0;
+}
+
+void stage_updated_gitmodules(void)
+{
+       if (add_file_to_cache(".gitmodules", 0))
+               die(_("staging updated .gitmodules failed"));
+}
+
 static int add_submodule_odb(const char *path)
 {
        struct strbuf objects_directory = STRBUF_INIT;
@@ -88,7 +178,7 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
 
 int submodule_config(const char *var, const char *value, void *cb)
 {
-       if (!prefixcmp(var, "submodule."))
+       if (starts_with(var, "submodule."))
                return parse_submodule_config_option(var, value);
        else if (!strcmp(var, "fetch.recursesubmodules")) {
                config_fetch_recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
@@ -116,6 +206,11 @@ void gitmodules_config(void)
                                    !memcmp(ce->name, ".gitmodules", 11))
                                        gitmodules_is_unmerged = 1;
                        }
+               } else if (pos < active_nr) {
+                       struct stat st;
+                       if (lstat(".gitmodules", &st) == 0 &&
+                           ce_match_stat(active_cache[pos], &st, 0) & DATA_CHANGED)
+                               gitmodules_is_modified = 1;
                }
 
                if (!gitmodules_is_unmerged)
@@ -126,45 +221,50 @@ void gitmodules_config(void)
 
 int parse_submodule_config_option(const char *var, const char *value)
 {
-       int len;
        struct string_list_item *config;
-       struct strbuf submodname = STRBUF_INIT;
+       const char *name, *key;
+       int namelen;
 
-       var += 10;              /* Skip "submodule." */
+       if (parse_config_key(var, "submodule", &name, &namelen, &key) < 0 || !name)
+               return 0;
+
+       if (!strcmp(key, "path")) {
+               if (!value)
+                       return config_error_nonbool(var);
 
-       len = strlen(var);
-       if ((len > 5) && !strcmp(var + len - 5, ".path")) {
-               strbuf_add(&submodname, var, len - 5);
                config = unsorted_string_list_lookup(&config_name_for_path, value);
                if (config)
                        free(config->util);
                else
                        config = string_list_append(&config_name_for_path, xstrdup(value));
-               config->util = strbuf_detach(&submodname, NULL);
-               strbuf_release(&submodname);
-       } else if ((len > 23) && !strcmp(var + len - 23, ".fetchrecursesubmodules")) {
-               strbuf_add(&submodname, var, len - 23);
-               config = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, submodname.buf);
+               config->util = xmemdupz(name, namelen);
+       } else if (!strcmp(key, "fetchrecursesubmodules")) {
+               char *name_cstr = xmemdupz(name, namelen);
+               config = unsorted_string_list_lookup(&config_fetch_recurse_submodules_for_name, name_cstr);
                if (!config)
-                       config = string_list_append(&config_fetch_recurse_submodules_for_name,
-                                                   strbuf_detach(&submodname, NULL));
+                       config = string_list_append(&config_fetch_recurse_submodules_for_name, name_cstr);
+               else
+                       free(name_cstr);
                config->util = (void *)(intptr_t)parse_fetch_recurse_submodules_arg(var, value);
-               strbuf_release(&submodname);
-       } else if ((len > 7) && !strcmp(var + len - 7, ".ignore")) {
+       } else if (!strcmp(key, "ignore")) {
+               char *name_cstr;
+
+               if (!value)
+                       return config_error_nonbool(var);
+
                if (strcmp(value, "untracked") && strcmp(value, "dirty") &&
                    strcmp(value, "all") && strcmp(value, "none")) {
                        warning("Invalid parameter \"%s\" for config option \"submodule.%s.ignore\"", value, var);
                        return 0;
                }
 
-               strbuf_add(&submodname, var, len - 7);
-               config = unsorted_string_list_lookup(&config_ignore_for_name, submodname.buf);
-               if (config)
+               name_cstr = xmemdupz(name, namelen);
+               config = unsorted_string_list_lookup(&config_ignore_for_name, name_cstr);
+               if (config) {
                        free(config->util);
-               else
-                       config = string_list_append(&config_ignore_for_name,
-                                                   strbuf_detach(&submodname, NULL));
-               strbuf_release(&submodname);
+                       free(name_cstr);
+               } else
+                       config = string_list_append(&config_ignore_for_name, name_cstr);
                config->util = xstrdup(value);
                return 0;
        }
@@ -217,6 +317,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
 }
 
 static void print_submodule_summary(struct rev_info *rev, FILE *f,
+               const char *line_prefix,
                const char *del, const char *add, const char *reset)
 {
        static const char format[] = "  %m %s";
@@ -226,7 +327,9 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
        while ((commit = get_revision(rev))) {
                struct pretty_print_context ctx = {0};
                ctx.date_mode = rev->date_mode;
+               ctx.output_encoding = get_log_output_encoding();
                strbuf_setlen(&sb, 0);
+               strbuf_addstr(&sb, line_prefix);
                if (commit->object.flags & SYMMETRIC_LEFT) {
                        if (del)
                                strbuf_addstr(&sb, del);
@@ -257,12 +360,13 @@ int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
 }
 
 void show_submodule_summary(FILE *f, const char *path,
+               const char *line_prefix,
                unsigned char one[20], unsigned char two[20],
-               unsigned dirty_submodule,
+               unsigned dirty_submodule, const char *meta,
                const char *del, const char *add, const char *reset)
 {
        struct rev_info rev;
-       struct commit *left = left, *right = right;
+       struct commit *left = NULL, *right = NULL;
        const char *message = NULL;
        struct strbuf sb = STRBUF_INIT;
        int fast_forward = 0, fast_backward = 0;
@@ -276,38 +380,39 @@ void show_submodule_summary(FILE *f, const char *path,
        else if (!(left = lookup_commit_reference(one)) ||
                 !(right = lookup_commit_reference(two)))
                message = "(commits not present)";
-
-       if (!message &&
-           prepare_submodule_summary(&rev, path, left, right,
-                                       &fast_forward, &fast_backward))
+       else if (prepare_submodule_summary(&rev, path, left, right,
+                                          &fast_forward, &fast_backward))
                message = "(revision walker failed)";
 
        if (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
-               fprintf(f, "Submodule %s contains untracked content\n", path);
+               fprintf(f, "%sSubmodule %s contains untracked content\n",
+                       line_prefix, path);
        if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
-               fprintf(f, "Submodule %s contains modified content\n", path);
+               fprintf(f, "%sSubmodule %s contains modified content\n",
+                       line_prefix, path);
 
        if (!hashcmp(one, two)) {
                strbuf_release(&sb);
                return;
        }
 
-       strbuf_addf(&sb, "Submodule %s %s..", path,
+       strbuf_addf(&sb, "%s%sSubmodule %s %s..", line_prefix, meta, path,
                        find_unique_abbrev(one, DEFAULT_ABBREV));
        if (!fast_backward && !fast_forward)
                strbuf_addch(&sb, '.');
        strbuf_addf(&sb, "%s", find_unique_abbrev(two, DEFAULT_ABBREV));
        if (message)
-               strbuf_addf(&sb, " %s\n", message);
+               strbuf_addf(&sb, " %s%s\n", message, reset);
        else
-               strbuf_addf(&sb, "%s:\n", fast_backward ? " (rewind)" : "");
+               strbuf_addf(&sb, "%s:%s\n", fast_backward ? " (rewind)" : "", reset);
        fwrite(sb.buf, sb.len, 1, f);
 
-       if (!message) {
-               print_submodule_summary(&rev, f, del, add, reset);
+       if (!message) /* only NULL if we succeeded in setting up the walk */
+               print_submodule_summary(&rev, f, line_prefix, del, add, reset);
+       if (left)
                clear_commit_marks(left, ~0);
+       if (right)
                clear_commit_marks(right, ~0);
-       }
 
        strbuf_release(&sb);
 }
@@ -600,9 +705,8 @@ int fetch_populated_submodules(const struct argv_array *options,
        if (!work_tree)
                goto out;
 
-       if (!the_index.initialized)
-               if (read_cache() < 0)
-                       die("index file corrupt");
+       if (read_cache() < 0)
+               die("index file corrupt");
 
        argv_array_push(&argv, "fetch");
        for (i = 0; i < options->argc; i++)
@@ -621,7 +725,7 @@ int fetch_populated_submodules(const struct argv_array *options,
                struct strbuf submodule_path = STRBUF_INIT;
                struct strbuf submodule_git_dir = STRBUF_INIT;
                struct strbuf submodule_prefix = STRBUF_INIT;
-               struct cache_entry *ce = active_cache[i];
+               const struct cache_entry *ce = active_cache[i];
                const char *git_dir, *name, *default_argv;
 
                if (!S_ISGITLINK(ce->ce_mode))
@@ -759,11 +863,91 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
        return dirty_submodule;
 }
 
+int submodule_uses_gitfile(const char *path)
+{
+       struct child_process cp;
+       const char *argv[] = {
+               "submodule",
+               "foreach",
+               "--quiet",
+               "--recursive",
+               "test -f .git",
+               NULL,
+       };
+       struct strbuf buf = STRBUF_INIT;
+       const char *git_dir;
+
+       strbuf_addf(&buf, "%s/.git", path);
+       git_dir = read_gitfile(buf.buf);
+       if (!git_dir) {
+               strbuf_release(&buf);
+               return 0;
+       }
+       strbuf_release(&buf);
+
+       /* Now test that all nested submodules use a gitfile too */
+       memset(&cp, 0, sizeof(cp));
+       cp.argv = argv;
+       cp.env = local_repo_env;
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       cp.no_stderr = 1;
+       cp.no_stdout = 1;
+       cp.dir = path;
+       if (run_command(&cp))
+               return 0;
+
+       return 1;
+}
+
+int ok_to_remove_submodule(const char *path)
+{
+       struct stat st;
+       ssize_t len;
+       struct child_process cp;
+       const char *argv[] = {
+               "status",
+               "--porcelain",
+               "-u",
+               "--ignore-submodules=none",
+               NULL,
+       };
+       struct strbuf buf = STRBUF_INIT;
+       int ok_to_remove = 1;
+
+       if ((lstat(path, &st) < 0) || is_empty_dir(path))
+               return 1;
+
+       if (!submodule_uses_gitfile(path))
+               return 0;
+
+       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 (start_command(&cp))
+               die("Could not run 'git status --porcelain -uall --ignore-submodules=none' in submodule %s", path);
+
+       len = strbuf_read(&buf, cp.out, 1024);
+       if (len > 2)
+               ok_to_remove = 0;
+       close(cp.out);
+
+       if (finish_command(&cp))
+               die("'git status --porcelain -uall --ignore-submodules=none' failed in submodule %s", path);
+
+       strbuf_release(&buf);
+       return ok_to_remove;
+}
+
 static int find_first_merges(struct object_array *result, const char *path,
                struct commit *a, struct commit *b)
 {
        int i, j;
-       struct object_array merges;
+       struct object_array merges = OBJECT_ARRAY_INIT;
        struct commit *commit;
        int contains_another;
 
@@ -773,7 +957,6 @@ static int find_first_merges(struct object_array *result, const char *path,
        struct rev_info revs;
        struct setup_revision_opt rev_opts;
 
-       memset(&merges, 0, sizeof(merges));
        memset(result, 0, sizeof(struct object_array));
        memset(&rev_opts, 0, sizeof(rev_opts));
 
@@ -811,8 +994,7 @@ static int find_first_merges(struct object_array *result, const char *path,
                }
 
                if (!contains_another)
-                       add_object_array(merges.objects[i].item,
-                                        merges.objects[i].name, result);
+                       add_object_array(merges.objects[i].item, NULL, result);
        }
 
        free(merges.objects);
@@ -923,3 +1105,34 @@ int merge_submodule(unsigned char result[20], const char *path,
        free(merges.objects);
        return 0;
 }
+
+/* Update gitfile and core.worktree setting to connect work tree and git dir */
+void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir)
+{
+       struct strbuf file_name = STRBUF_INIT;
+       struct strbuf rel_path = STRBUF_INIT;
+       const char *real_work_tree = xstrdup(real_path(work_tree));
+       FILE *fp;
+
+       /* Update gitfile */
+       strbuf_addf(&file_name, "%s/.git", work_tree);
+       fp = fopen(file_name.buf, "w");
+       if (!fp)
+               die(_("Could not create git link %s"), file_name.buf);
+       fprintf(fp, "gitdir: %s\n", relative_path(git_dir, real_work_tree,
+                                                 &rel_path));
+       fclose(fp);
+
+       /* Update core.worktree setting */
+       strbuf_reset(&file_name);
+       strbuf_addf(&file_name, "%s/config", git_dir);
+       if (git_config_set_in_file(file_name.buf, "core.worktree",
+                                  relative_path(real_work_tree, git_dir,
+                                                &rel_path)))
+               die(_("Could not set core.worktree in %s"),
+                   file_name.buf);
+
+       strbuf_release(&file_name);
+       strbuf_release(&rel_path);
+       free((void *)real_work_tree);
+}