Signed-off-by: Stefan Beller <sbeller@xxxxxxxxxx> --- Marking this as RFC as documentation and tests are missing. builtin/rev-parse.c | 7 +++++ submodule.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++ submodule.h | 8 +++++ 3 files changed, 102 insertions(+) diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c index e08677e559..2549643267 100644 --- a/builtin/rev-parse.c +++ b/builtin/rev-parse.c @@ -12,6 +12,7 @@ #include "diff.h" #include "revision.h" #include "split-index.h" +#include "submodule.h" #define DO_REVS 1 #define DO_NOREV 2 @@ -779,6 +780,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) puts(work_tree); continue; } + if (!strcmp(arg, "--show-superproject-working-tree")) { + const char *superproject = get_superproject_working_tree(); + if (superproject) + puts(superproject); + continue; + } if (!strcmp(arg, "--show-prefix")) { if (prefix) puts(prefix); diff --git a/submodule.c b/submodule.c index 3b98766a6b..a63aef2c6b 100644 --- a/submodule.c +++ b/submodule.c @@ -1514,3 +1514,90 @@ void absorb_git_dir_into_superproject(const char *prefix, strbuf_release(&sb); } } + +static int superproject_exists(void) +{ + struct child_process cp = CHILD_PROCESS_INIT; + struct strbuf sb = STRBUF_INIT; + struct strbuf buf = STRBUF_INIT; + const char *one_up = real_path_if_valid("../"); + const char *dirname; + int code, has_superproject = 0; + + if (!one_up) + /* At the root of the file system. */ + return 0; + + dirname = relative_path(xgetcwd(), 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-tree", "HEAD", "--", dirname, NULL); + + cp.no_stdin = 1; + cp.no_stderr = 1; + cp.out = -1; + cp.git_cmd = 1; + + if (start_command(&cp)) + die(_("could not start ls-tree in ..")); + + strbuf_read(&buf, cp.out, 7); + close(cp.out); + if (starts_with(buf.buf, "160000")) + /* there is a superproject having this as a submodule */ + has_superproject = 1; + + code = finish_command(&cp); + + if (code == 128) + /* not a git repository */ + goto out; + if (code == 0 && !has_superproject) + /* there is an unrelated git repository */ + goto out; + + if (code) + die(_("ls-tree returned unexpected return code")); + + return 1; + +out: + strbuf_release(&sb); + + return 0; +} + +const char *get_superproject_working_tree() +{ + struct child_process cp = CHILD_PROCESS_INIT; + struct strbuf sb = STRBUF_INIT; + + if (!superproject_exists()) + return NULL; + + prepare_submodule_repo_env(&cp.env_array); + argv_array_pop(&cp.env_array); + + argv_array_pushl(&cp.args, "-C", "..", + "rev-parse", "--show-toplevel", NULL); + + cp.no_stdin = 1; + cp.no_stderr = 1; + cp.out = -1; + cp.git_cmd = 1; + + if (start_command(&cp)) + die(_("could not start rev-parse in ..")); + + strbuf_reset(&sb); + strbuf_read(&sb, cp.out, PATH_MAX); + + /* remove trailing new line */ + strbuf_rtrim(&sb); + + if (finish_command(&cp)) + die(_("rev-parse died unexpectedly")); + + return strbuf_detach(&sb, NULL); +} diff --git a/submodule.h b/submodule.h index 05ab674f06..f207bb8d5f 100644 --- a/submodule.h +++ b/submodule.h @@ -93,4 +93,12 @@ extern void prepare_submodule_repo_env(struct argv_array *out); extern void absorb_git_dir_into_superproject(const char *prefix, const char *path, unsigned flags); + +/* + * Return the absolute path of the working tree of the superproject, which this + * project is a submodule of. If this repository is not a submodule of + * another repository, return NULL. + */ +extern const char *get_superproject_working_tree(); + #endif -- 2.12.0.189.g3bc53220cb.dirty