Skip to content

Commit ee6fc51

Browse files
jlehmanngitster
authored andcommitted
Show submodules as modified when they contain a dirty work tree
Until now a submodule only then showed up as modified in the supermodule when the last commit in the submodule differed from the one in the index or the diffed against commit of the superproject. A dirty work tree containing new untracked or modified files in a submodule was undetectable when looking at it from the superproject. Now git status and git diff (against the work tree) in the superproject will also display submodules as modified when they contain untracked or modified files, even if the compared ref matches the HEAD of the submodule. Signed-off-by: Jens Lehmann <[email protected]> Signed-off-by: Nanako Shiraishi <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1f73566 commit ee6fc51

File tree

4 files changed

+124
-17
lines changed

4 files changed

+124
-17
lines changed

diff-lib.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "cache-tree.h"
1111
#include "unpack-trees.h"
1212
#include "refs.h"
13+
#include "submodule.h"
1314

1415
/*
1516
* diff-files
@@ -159,7 +160,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
159160
continue;
160161
}
161162

162-
if (ce_uptodate(ce) || ce_skip_worktree(ce))
163+
if ((ce_uptodate(ce) && !S_ISGITLINK(ce->ce_mode)) || ce_skip_worktree(ce))
163164
continue;
164165

165166
/* If CE_VALID is set, don't look at workdir for file removal */
@@ -176,6 +177,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
176177
continue;
177178
}
178179
changed = ce_match_stat(ce, &st, ce_option);
180+
if (S_ISGITLINK(ce->ce_mode) && !changed)
181+
changed = is_submodule_modified(ce->name);
179182
if (!changed) {
180183
ce_mark_uptodate(ce);
181184
if (!DIFF_OPT_TST(&revs->diffopt, FIND_COPIES_HARDER))
@@ -230,7 +233,8 @@ static int get_stat_data(struct cache_entry *ce,
230233
return -1;
231234
}
232235
changed = ce_match_stat(ce, &st, 0);
233-
if (changed) {
236+
if (changed
237+
|| (S_ISGITLINK(ce->ce_mode) && is_submodule_modified(ce->name))) {
234238
mode = ce_mode_from_stat(ce, st.st_mode);
235239
sha1 = null_sha1;
236240
}

submodule.c

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "diff.h"
55
#include "commit.h"
66
#include "revision.h"
7+
#include "run-command.h"
78

89
int add_submodule_odb(const char *path)
910
{
@@ -112,3 +113,51 @@ void show_submodule_summary(FILE *f, const char *path,
112113
}
113114
strbuf_release(&sb);
114115
}
116+
117+
int is_submodule_modified(const char *path)
118+
{
119+
int len;
120+
struct child_process cp;
121+
const char *argv[] = {
122+
"status",
123+
"--porcelain",
124+
NULL,
125+
};
126+
char *env[3];
127+
struct strbuf buf = STRBUF_INIT;
128+
129+
strbuf_addf(&buf, "%s/.git/", path);
130+
if (!is_directory(buf.buf)) {
131+
strbuf_release(&buf);
132+
/* The submodule is not checked out, so it is not modified */
133+
return 0;
134+
135+
}
136+
strbuf_reset(&buf);
137+
138+
strbuf_addf(&buf, "GIT_WORK_TREE=%s", path);
139+
env[0] = strbuf_detach(&buf, NULL);
140+
strbuf_addf(&buf, "GIT_DIR=%s/.git", path);
141+
env[1] = strbuf_detach(&buf, NULL);
142+
env[2] = NULL;
143+
144+
memset(&cp, 0, sizeof(cp));
145+
cp.argv = argv;
146+
cp.env = (const char *const *)env;
147+
cp.git_cmd = 1;
148+
cp.no_stdin = 1;
149+
cp.out = -1;
150+
if (start_command(&cp))
151+
die("Could not run git status --porcelain");
152+
153+
len = strbuf_read(&buf, cp.out, 1024);
154+
close(cp.out);
155+
156+
if (finish_command(&cp))
157+
die("git status --porcelain failed");
158+
159+
free(env[0]);
160+
free(env[1]);
161+
strbuf_release(&buf);
162+
return len != 0;
163+
}

submodule.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
void show_submodule_summary(FILE *f, const char *path,
55
unsigned char one[20], unsigned char two[20],
66
const char *del, const char *add, const char *reset);
7+
int is_submodule_modified(const char *path);
78

89
#endif

t/t7506-status-submodule.sh

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,87 @@ test_description='git status for submodule'
55
. ./test-lib.sh
66

77
test_expect_success 'setup' '
8-
test_create_repo sub
9-
cd sub &&
10-
: >bar &&
11-
git add bar &&
12-
git commit -m " Add bar" &&
13-
cd .. &&
14-
git add sub &&
8+
test_create_repo sub &&
9+
(
10+
cd sub &&
11+
: >bar &&
12+
git add bar &&
13+
git commit -m " Add bar" &&
14+
: >foo &&
15+
git add foo &&
16+
git commit -m " Add foo"
17+
) &&
18+
echo output > .gitignore &&
19+
git add sub .gitignore &&
1520
git commit -m "Add submodule sub"
1621
'
1722

1823
test_expect_success 'status clean' '
19-
git status |
20-
grep "nothing to commit"
24+
git status >output &&
25+
grep "nothing to commit" output
2126
'
27+
2228
test_expect_success 'commit --dry-run -a clean' '
23-
git commit --dry-run -a |
24-
grep "nothing to commit"
29+
test_must_fail git commit --dry-run -a >output &&
30+
grep "nothing to commit" output
31+
'
32+
33+
test_expect_success 'status with modified file in submodule' '
34+
(cd sub && git reset --hard) &&
35+
echo "changed" >sub/foo &&
36+
git status >output &&
37+
grep "modified: sub" output
38+
'
39+
40+
test_expect_success 'status with modified file in submodule (porcelain)' '
41+
(cd sub && git reset --hard) &&
42+
echo "changed" >sub/foo &&
43+
git status --porcelain >output &&
44+
diff output - <<-\EOF
45+
M sub
46+
EOF
47+
'
48+
49+
test_expect_success 'status with added file in submodule' '
50+
(cd sub && git reset --hard && echo >foo && git add foo) &&
51+
git status >output &&
52+
grep "modified: sub" output
53+
'
54+
55+
test_expect_success 'status with added file in submodule (porcelain)' '
56+
(cd sub && git reset --hard && echo >foo && git add foo) &&
57+
git status --porcelain >output &&
58+
diff output - <<-\EOF
59+
M sub
60+
EOF
61+
'
62+
63+
test_expect_success 'status with untracked file in submodule' '
64+
(cd sub && git reset --hard) &&
65+
echo "content" >sub/new-file &&
66+
git status >output &&
67+
grep "modified: sub" output
68+
'
69+
70+
test_expect_success 'status with untracked file in submodule (porcelain)' '
71+
git status --porcelain >output &&
72+
diff output - <<-\EOF
73+
M sub
74+
EOF
2575
'
76+
2677
test_expect_success 'rm submodule contents' '
2778
rm -rf sub/* sub/.git
2879
'
80+
2981
test_expect_success 'status clean (empty submodule dir)' '
30-
git status |
31-
grep "nothing to commit"
82+
git status >output &&
83+
grep "nothing to commit" output
3284
'
85+
3386
test_expect_success 'status -a clean (empty submodule dir)' '
34-
git commit --dry-run -a |
35-
grep "nothing to commit"
87+
test_must_fail git commit --dry-run -a >output &&
88+
grep "nothing to commit" output
3689
'
3790

3891
test_done

0 commit comments

Comments
 (0)