Skip to content

Commit bf0231c

Browse files
stefanbellergitster
authored andcommitted
rev-parse: add --show-superproject-working-tree
In some situations it is useful to know if the given repository is a submodule of another repository. Add the flag --show-superproject-working-tree to git-rev-parse to make it easy to find out if there is a superproject. When no superproject exists, the output will be empty. Signed-off-by: Stefan Beller <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e0688e9 commit bf0231c

File tree

5 files changed

+117
-0
lines changed

5 files changed

+117
-0
lines changed

Documentation/git-rev-parse.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,12 @@ print a message to stderr and exit with nonzero status.
261261
--show-toplevel::
262262
Show the absolute path of the top-level directory.
263263

264+
--show-superproject-working-tree
265+
Show the absolute path of the root of the superproject's
266+
working tree (if exists) that uses the current repository as
267+
its submodule. Outputs nothing if the current repository is
268+
not used as a submodule by any project.
269+
264270
--shared-index-path::
265271
Show the path to the shared index file in split index mode, or
266272
empty if not in split-index mode.

builtin/rev-parse.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "diff.h"
1313
#include "revision.h"
1414
#include "split-index.h"
15+
#include "submodule.h"
1516

1617
#define DO_REVS 1
1718
#define DO_NOREV 2
@@ -779,6 +780,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
779780
puts(work_tree);
780781
continue;
781782
}
783+
if (!strcmp(arg, "--show-superproject-working-tree")) {
784+
const char *superproject = get_superproject_working_tree();
785+
if (superproject)
786+
puts(superproject);
787+
continue;
788+
}
782789
if (!strcmp(arg, "--show-prefix")) {
783790
if (prefix)
784791
puts(prefix);

submodule.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1514,3 +1514,85 @@ void absorb_git_dir_into_superproject(const char *prefix,
15141514
strbuf_release(&sb);
15151515
}
15161516
}
1517+
1518+
const char *get_superproject_working_tree(void)
1519+
{
1520+
struct child_process cp = CHILD_PROCESS_INIT;
1521+
struct strbuf sb = STRBUF_INIT;
1522+
const char *one_up = real_path_if_valid("../");
1523+
const char *cwd = xgetcwd();
1524+
const char *ret = NULL;
1525+
const char *subpath;
1526+
int code;
1527+
ssize_t len;
1528+
1529+
if (!is_inside_work_tree())
1530+
/*
1531+
* FIXME:
1532+
* We might have a superproject, but it is harder
1533+
* to determine.
1534+
*/
1535+
return NULL;
1536+
1537+
if (!one_up)
1538+
return NULL;
1539+
1540+
subpath = relative_path(cwd, one_up, &sb);
1541+
1542+
prepare_submodule_repo_env(&cp.env_array);
1543+
argv_array_pop(&cp.env_array);
1544+
1545+
argv_array_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
1546+
"ls-files", "-z", "--stage", "--full-name", "--",
1547+
subpath, NULL);
1548+
strbuf_reset(&sb);
1549+
1550+
cp.no_stdin = 1;
1551+
cp.no_stderr = 1;
1552+
cp.out = -1;
1553+
cp.git_cmd = 1;
1554+
1555+
if (start_command(&cp))
1556+
die(_("could not start ls-files in .."));
1557+
1558+
len = strbuf_read(&sb, cp.out, PATH_MAX);
1559+
close(cp.out);
1560+
1561+
if (starts_with(sb.buf, "160000")) {
1562+
int super_sub_len;
1563+
int cwd_len = strlen(cwd);
1564+
char *super_sub, *super_wt;
1565+
1566+
/*
1567+
* There is a superproject having this repo as a submodule.
1568+
* The format is <mode> SP <hash> SP <stage> TAB <full name> \0,
1569+
* We're only interested in the name after the tab.
1570+
*/
1571+
super_sub = strchr(sb.buf, '\t') + 1;
1572+
super_sub_len = sb.buf + sb.len - super_sub - 1;
1573+
1574+
if (super_sub_len > cwd_len ||
1575+
strcmp(&cwd[cwd_len - super_sub_len], super_sub))
1576+
die (_("BUG: returned path string doesn't match cwd?"));
1577+
1578+
super_wt = xstrdup(cwd);
1579+
super_wt[cwd_len - super_sub_len] = '\0';
1580+
1581+
ret = real_path(super_wt);
1582+
free(super_wt);
1583+
}
1584+
strbuf_release(&sb);
1585+
1586+
code = finish_command(&cp);
1587+
1588+
if (code == 128)
1589+
/* '../' is not a git repository */
1590+
return NULL;
1591+
if (code == 0 && len == 0)
1592+
/* There is an unrelated git repository at '../' */
1593+
return NULL;
1594+
if (code)
1595+
die(_("ls-tree returned unexpected return code %d"), code);
1596+
1597+
return ret;
1598+
}

submodule.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,12 @@ extern void prepare_submodule_repo_env(struct argv_array *out);
9393
extern void absorb_git_dir_into_superproject(const char *prefix,
9494
const char *path,
9595
unsigned flags);
96+
97+
/*
98+
* Return the absolute path of the working tree of the superproject, which this
99+
* project is a submodule of. If this repository is not a submodule of
100+
* another repository, return NULL.
101+
*/
102+
extern const char *get_superproject_working_tree(void);
103+
96104
#endif

t/t1500-rev-parse.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,18 @@ test_expect_success 'git-path inside sub-dir' '
116116
test_cmp expect actual
117117
'
118118

119+
test_expect_success 'showing the superproject correctly' '
120+
git rev-parse --show-superproject-working-tree >out &&
121+
test_must_be_empty out &&
122+
123+
test_create_repo super &&
124+
test_commit -C super test_commit &&
125+
test_create_repo sub &&
126+
test_commit -C sub test_commit &&
127+
git -C super submodule add ../sub dir/sub &&
128+
echo $(pwd)/super >expect &&
129+
git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
130+
test_cmp expect out
131+
'
132+
119133
test_done

0 commit comments

Comments
 (0)