diff options
author | Jeff King <peff@peff.net> | 2012-10-29 12:12:07 +0400 |
---|---|---|
committer | Jeff King <peff@peff.net> | 2012-10-29 12:12:07 +0400 |
commit | d21240fafafdea4fb4cab27c0e9b58ebad7d6172 (patch) | |
tree | 834ae033a8df4979709fd58a72f08d9ab7a21e8d /submodule.c | |
parent | 2cfceefaca16d64baecf0ba9bcd4e05229d9c31b (diff) | |
parent | 293ab15eea341ffe8705bac99136f2e3a286db5f (diff) |
Merge branch 'jl/submodule-rm'
"git rm submodule" cannot blindly remove a submodule directory as
its working tree may have local changes, and worse yet, it may even
have its repository embedded in it. Teach it some special cases
where it is safe to remove a submodule, specifically, when there is
no local changes in the submodule working tree, and its repository
is not embedded in its working tree but is elsewhere and uses the
gitfile mechanism to point at it.
* jl/submodule-rm:
submodule: teach rm to remove submodules unless they contain a git directory
Diffstat (limited to 'submodule.c')
-rw-r--r-- | submodule.c | 80 |
1 files changed, 80 insertions, 0 deletions
diff --git a/submodule.c b/submodule.c index 50f213e9263..e3e0b455eae 100644 --- a/submodule.c +++ b/submodule.c @@ -759,6 +759,86 @@ 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) { |