submodule.c: convert is_submodule_modified to use strbuf_getwholeline
[gitweb.git] / submodule.c
index 3b98766a6bcfe983e5f94e84edd6fcec7ce74f8d..e72781d9f21d6c2745e6679726dfc698aa3d1720 100644 (file)
@@ -1041,18 +1041,12 @@ int fetch_populated_submodules(const struct argv_array *options,
 
 unsigned is_submodule_modified(const char *path, int ignore_untracked)
 {
-       ssize_t len;
        struct child_process cp = CHILD_PROCESS_INIT;
-       const char *argv[] = {
-               "status",
-               "--porcelain",
-               NULL,
-               NULL,
-       };
        struct strbuf buf = STRBUF_INIT;
+       FILE *fp;
        unsigned dirty_submodule = 0;
-       const char *line, *next_line;
        const char *git_dir;
+       int ignore_cp_exit_code = 0;
 
        strbuf_addf(&buf, "%s/.git", path);
        git_dir = read_gitfile(buf.buf);
@@ -1066,10 +1060,10 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
        }
        strbuf_reset(&buf);
 
+       argv_array_pushl(&cp.args, "status", "--porcelain", NULL);
        if (ignore_untracked)
-               argv[2] = "-uno";
+               argv_array_push(&cp.args, "-uno");
 
-       cp.argv = argv;
        prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
@@ -1078,29 +1072,27 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
        if (start_command(&cp))
                die("Could not run 'git status --porcelain' in submodule %s", path);
 
-       len = strbuf_read(&buf, cp.out, 1024);
-       line = buf.buf;
-       while (len > 2) {
-               if ((line[0] == '?') && (line[1] == '?')) {
+       fp = xfdopen(cp.out, "r");
+       while (strbuf_getwholeline(&buf, fp, '\n') != EOF) {
+               if ((buf.buf[0] == '?') && (buf.buf[1] == '?'))
                        dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
-                       if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
-                               break;
-               } else {
+               else
                        dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
-                       if (ignore_untracked ||
-                           (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED))
-                               break;
-               }
-               next_line = strchr(line, '\n');
-               if (!next_line)
+
+               if ((dirty_submodule & DIRTY_SUBMODULE_MODIFIED) &&
+                   ((dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) ||
+                    ignore_untracked)) {
+                       /*
+                        * We're not interested in any further information from
+                        * the child any more, neither output nor its exit code.
+                        */
+                       ignore_cp_exit_code = 1;
                        break;
-               next_line++;
-               len -= (next_line - line);
-               line = next_line;
+               }
        }
-       close(cp.out);
+       fclose(fp);
 
-       if (finish_command(&cp))
+       if (finish_command(&cp) && !ignore_cp_exit_code)
                die("'git status --porcelain' failed in submodule %s", path);
 
        strbuf_release(&buf);
@@ -1403,7 +1395,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
                /* If it is an actual gitfile, it doesn't need migration. */
                return;
 
-       real_old_git_dir = real_pathdup(old_git_dir);
+       real_old_git_dir = real_pathdup(old_git_dir, 1);
 
        sub = submodule_from_path(null_sha1, path);
        if (!sub)
@@ -1412,7 +1404,7 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
        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);
+       real_new_git_dir = real_pathdup(new_git_dir, 1);
 
        if (!prefix)
                prefix = get_super_prefix();
@@ -1472,14 +1464,14 @@ void absorb_git_dir_into_superproject(const char *prefix,
                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);
+               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);
        } else {
                /* Is it already absorbed into the superprojects git dir? */
-               char *real_sub_git_dir = real_pathdup(sub_git_dir);
-               char *real_common_git_dir = real_pathdup(get_git_common_dir());
+               char *real_sub_git_dir = real_pathdup(sub_git_dir, 1);
+               char *real_common_git_dir = real_pathdup(get_git_common_dir(), 1);
 
                if (!starts_with(real_sub_git_dir, real_common_git_dir))
                        relocate_single_git_dir_into_superproject(prefix, path);
@@ -1514,3 +1506,85 @@ void absorb_git_dir_into_superproject(const char *prefix,
                strbuf_release(&sb);
        }
 }
+
+const char *get_superproject_working_tree(void)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct strbuf sb = STRBUF_INIT;
+       const char *one_up = real_path_if_valid("../");
+       const char *cwd = xgetcwd();
+       const char *ret = NULL;
+       const char *subpath;
+       int code;
+       ssize_t len;
+
+       if (!is_inside_work_tree())
+               /*
+                * FIXME:
+                * We might have a superproject, but it is harder
+                * to determine.
+                */
+               return NULL;
+
+       if (!one_up)
+               return NULL;
+
+       subpath = relative_path(cwd, one_up, &sb);
+
+       prepare_submodule_repo_env(&cp.env_array);
+       argv_array_pop(&cp.env_array);
+
+       argv_array_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
+                       "ls-files", "-z", "--stage", "--full-name", "--",
+                       subpath, NULL);
+       strbuf_reset(&sb);
+
+       cp.no_stdin = 1;
+       cp.no_stderr = 1;
+       cp.out = -1;
+       cp.git_cmd = 1;
+
+       if (start_command(&cp))
+               die(_("could not start ls-files in .."));
+
+       len = strbuf_read(&sb, cp.out, PATH_MAX);
+       close(cp.out);
+
+       if (starts_with(sb.buf, "160000")) {
+               int super_sub_len;
+               int cwd_len = strlen(cwd);
+               char *super_sub, *super_wt;
+
+               /*
+                * There is a superproject having this repo as a submodule.
+                * The format is <mode> SP <hash> SP <stage> TAB <full name> \0,
+                * We're only interested in the name after the tab.
+                */
+               super_sub = strchr(sb.buf, '\t') + 1;
+               super_sub_len = sb.buf + sb.len - super_sub - 1;
+
+               if (super_sub_len > cwd_len ||
+                   strcmp(&cwd[cwd_len - super_sub_len], super_sub))
+                       die (_("BUG: returned path string doesn't match cwd?"));
+
+               super_wt = xstrdup(cwd);
+               super_wt[cwd_len - super_sub_len] = '\0';
+
+               ret = real_path(super_wt);
+               free(super_wt);
+       }
+       strbuf_release(&sb);
+
+       code = finish_command(&cp);
+
+       if (code == 128)
+               /* '../' is not a git repository */
+               return NULL;
+       if (code == 0 && len == 0)
+               /* There is an unrelated git repository at '../' */
+               return NULL;
+       if (code)
+               die(_("ls-tree returned unexpected return code %d"), code);
+
+       return ret;
+}