diff: teach diff to display submodule difference with an inline diff
Teach git-diff and friends a new format for displaying the difference of a submodule. The new format is an inline diff of the contents of the submodule between the commit range of the update. This allows the user to see the actual code change caused by a submodule update. Add tests for the new format and option. Signed-off-by: Jacob Keller <jacob.keller@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Jacob Keller committed
Aug 31, 2016 at 16:27 UTC
fd47ae6a5b9cc0cfc56c1f7c43db612d26ca4b75
7 files changed
+863
-21
Documentation/diff-config.txt
+5
-4
@@ -122,10 +122,11 @@ diff.suppressBlankEmpty::
122
123
diff.submodule::
124
Specify the format in which differences in submodules are
125
- shown. The "log" format lists the commits in the range like
126
- linkgit:git-submodule[1] `summary` does. The "short" format
127
- format just shows the names of the commits at the beginning
128
- and end of the range. Defaults to short.
125
+ shown. The "short" format just shows the names of the commits
126
+ at the beginning and end of the range. The "log" format lists
127
+ the commits in the range like linkgit:git-submodule[1] `summary`
128
+ does. The "diff" format shows an inline diff of the changed
129
+ contents of the submodule. Defaults to "short".
130
131
diff.wordRegex::
132
A POSIX Extended Regular Expression used to determine what is a "word"
Documentation/diff-options.txt
+10
-7
@@ -210,13 +210,16 @@ any of those replacements occurred.
210
of the `--diff-filter` option on what the status letters mean.
211
212
--submodule[=<format>]::
213
- Specify how differences in submodules are shown. When `--submodule`
214
- or `--submodule=log` is given, the 'log' format is used. This format lists
215
- the commits in the range like linkgit:git-submodule[1] `summary` does.
216
- Omitting the `--submodule` option or specifying `--submodule=short`,
217
- uses the 'short' format. This format just shows the names of the commits
218
- at the beginning and end of the range. Can be tweaked via the
219
- `diff.submodule` configuration variable.
213
+ Specify how differences in submodules are shown. When specifying
214
+ `--submodule=short` the 'short' format is used. This format just
215
+ shows the names of the commits at the beginning and end of the range.
216
+ When `--submodule` or `--submodule=log` is specified, the 'log'
217
+ format is used. This format lists the commits in the range like
218
+ linkgit:git-submodule[1] `summary` does. When `--submodule=diff`
219
+ is specified, the 'diff' format is used. This format shows an
220
+ inline diff of the changes in the submodule contents between the
221
+ commit range. Defaults to `diff.submodule` or the 'short' format
222
+ if the config option is unset.
223
224
--color[=<when>]::
225
Show colored diff.
diff.c
+22
-9
@@ -135,6 +135,8 @@ static int parse_submodule_params(struct diff_options *options, const char *valu
135
options->submodule_format = DIFF_SUBMODULE_LOG;
136
else if (!strcmp(value, "short"))
137
options->submodule_format = DIFF_SUBMODULE_SHORT;
138
+ else if (!strcmp(value, "diff"))
139
+ options->submodule_format = DIFF_SUBMODULE_INLINE_DIFF;
140
else
141
return -1;
142
return 0;
@@ -2300,6 +2302,15 @@ static void builtin_diff(const char *name_a,
2302
struct strbuf header = STRBUF_INIT;
2303
const char *line_prefix = diff_line_prefix(o);
2304
2305
+ diff_set_mnemonic_prefix(o, "a/", "b/");
2306
+ if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
2307
+ a_prefix = o->b_prefix;
2308
+ b_prefix = o->a_prefix;
2309
+ } else {
2310
+ a_prefix = o->a_prefix;
2311
+ b_prefix = o->b_prefix;
2312
+ }
2313
+
2314
if (o->submodule_format == DIFF_SUBMODULE_LOG &&
2315
(!one->mode || S_ISGITLINK(one->mode)) &&
2316
(!two->mode || S_ISGITLINK(two->mode))) {
@@ -2311,6 +2322,17 @@ static void builtin_diff(const char *name_a,
2322
two->dirty_submodule,
2323
meta, del, add, reset);
2324
return;
2325
+ } else if (o->submodule_format == DIFF_SUBMODULE_INLINE_DIFF &&
2326
+ (!one->mode || S_ISGITLINK(one->mode)) &&
2327
+ (!two->mode || S_ISGITLINK(two->mode))) {
2328
+ const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
2329
+ const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
2330
+ show_submodule_inline_diff(o->file, one->path ? one->path : two->path,
2331
+ line_prefix,
2332
+ &one->oid, &two->oid,
2333
+ two->dirty_submodule,
2334
+ meta, del, add, reset, o);
2335
+ return;
2336
}
2337
2338
if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
@@ -2318,15 +2340,6 @@ static void builtin_diff(const char *name_a,
2340
textconv_two = get_textconv(two);
2341
}
2342
2321
- diff_set_mnemonic_prefix(o, "a/", "b/");
2322
- if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
2323
- a_prefix = o->b_prefix;
2324
- b_prefix = o->a_prefix;
2325
- } else {
2326
- a_prefix = o->a_prefix;
2327
- b_prefix = o->b_prefix;
2328
- }
2329
-
2343
/* Never use a non-valid filename anywhere if at all possible */
2344
name_a = DIFF_FILE_VALID(one) ? name_a : name_b;
2345
name_b = DIFF_FILE_VALID(two) ? name_b : name_a;
diff.h
+2
-1
@@ -111,7 +111,8 @@ enum diff_words_type {
111
112
enum diff_submodule_format {
113
DIFF_SUBMODULE_SHORT = 0,
114
- DIFF_SUBMODULE_LOG
114
+ DIFF_SUBMODULE_LOG,
115
+ DIFF_SUBMODULE_INLINE_DIFF
116
};
117
118
struct diff_options {
submodule.c
+69
@@ -442,6 +442,75 @@ out:
442
clear_commit_marks(right, ~0);
443
}
444
445
+void show_submodule_inline_diff(FILE *f, const char *path,
446
+ const char *line_prefix,
447
+ struct object_id *one, struct object_id *two,
448
+ unsigned dirty_submodule, const char *meta,
449
+ const char *del, const char *add, const char *reset,
450
+ const struct diff_options *o)
451
+{
452
+ const struct object_id *old = &empty_tree_oid, *new = &empty_tree_oid;
453
+ struct commit *left = NULL, *right = NULL;
454
+ struct commit_list *merge_bases = NULL;
455
+ struct strbuf submodule_dir = STRBUF_INIT;
456
+ struct child_process cp = CHILD_PROCESS_INIT;
457
+
458
+ show_submodule_header(f, path, line_prefix, one, two, dirty_submodule,
459
+ meta, reset, &left, &right, &merge_bases);
460
+
461
+ /* We need a valid left and right commit to display a difference */
462
+ if (!(left || is_null_oid(one)) ||
463
+ !(right || is_null_oid(two)))
464
+ goto done;
465
+
466
+ if (left)
467
+ old = one;
468
+ if (right)
469
+ new = two;
470
+
471
+ fflush(f);
472
+ cp.git_cmd = 1;
473
+ cp.dir = path;
474
+ cp.out = dup(fileno(f));
475
+ cp.no_stdin = 1;
476
+
477
+ /* TODO: other options may need to be passed here. */
478
+ argv_array_push(&cp.args, "diff");
479
+ argv_array_pushf(&cp.args, "--line-prefix=%s", line_prefix);
480
+ if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
481
+ argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
482
+ o->b_prefix, path);
483
+ argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
484
+ o->a_prefix, path);
485
+ } else {
486
+ argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
487
+ o->a_prefix, path);
488
+ argv_array_pushf(&cp.args, "--dst-prefix=%s%s/",
489
+ o->b_prefix, path);
490
+ }
491
+ argv_array_push(&cp.args, oid_to_hex(old));
492
+ /*
493
+ * If the submodule has modified content, we will diff against the
494
+ * work tree, under the assumption that the user has asked for the
495
+ * diff format and wishes to actually see all differences even if they
496
+ * haven't yet been committed to the submodule yet.
497
+ */
498
+ if (!(dirty_submodule & DIRTY_SUBMODULE_MODIFIED))
499
+ argv_array_push(&cp.args, oid_to_hex(new));
500
+
501
+ if (run_command(&cp))
502
+ fprintf(f, "(diff failed)\n");
503
+
504
+done:
505
+ strbuf_release(&submodule_dir);
506
+ if (merge_bases)
507
+ free_commit_list(merge_bases);
508
+ if (left)
509
+ clear_commit_marks(left, ~0);
510
+ if (right)
511
+ clear_commit_marks(right, ~0);
512
+}
513
+
514
void set_config_fetch_recurse_submodules(int value)
515
{
516
config_fetch_recurse_submodules = value;
submodule.h
+6
@@ -46,6 +46,12 @@ void show_submodule_summary(FILE *f, const char *path,
46
struct object_id *one, struct object_id *two,
47
unsigned dirty_submodule, const char *meta,
48
const char *del, const char *add, const char *reset);
49
+void show_submodule_inline_diff(FILE *f, const char *path,
50
+ const char *line_prefix,
51
+ struct object_id *one, struct object_id *two,
52
+ unsigned dirty_submodule, const char *meta,
53
+ const char *del, const char *add, const char *reset,
54
+ const struct diff_options *opt);
55
void set_config_fetch_recurse_submodules(int value);
56
void check_for_new_submodule_commits(unsigned char new_sha1[20]);
57
int fetch_populated_submodules(const struct argv_array *options,
t/t4060-diff-submodule-option-diff-format.sh
new
+749
@@ -0,0 +1,749 @@
1
+#!/bin/sh
2
+#
3
+# Copyright (c) 2009 Jens Lehmann, based on t7401 by Ping Yin
4
+# Copyright (c) 2011 Alexey Shumkin (+ non-UTF-8 commit encoding tests)
5
+# Copyright (c) 2016 Jacob Keller (copy + convert to --submodule=diff)
6
+#
7
+
8
+test_description='Support for diff format verbose submodule difference in git diff
9
+
10
+This test tries to verify the sanity of --submodule=diff option of git diff.
11
+'
12
+
13
+. ./test-lib.sh
14
+
15
+# Tested non-UTF-8 encoding
16
+test_encoding="ISO8859-1"
17
+
18
+# String "added" in German (translated with Google Translate), encoded in UTF-8,
19
+# used in sample commit log messages in add_file() function below.
20
+added=$(printf "hinzugef\303\274gt")
21
+
22
+add_file () {
23
+ (
24
+ cd "$1" &&
25
+ shift &&
26
+ for name
27
+ do
28
+ echo "$name" >"$name" &&
29
+ git add "$name" &&
30
+ test_tick &&
31
+ # "git commit -m" would break MinGW, as Windows refuse to pass
32
+ # $test_encoding encoded parameter to git.
33
+ echo "Add $name ($added $name)" | iconv -f utf-8 -t $test_encoding |
34
+ git -c "i18n.commitEncoding=$test_encoding" commit -F -
35
+ done >/dev/null &&
36
+ git rev-parse --short --verify HEAD
37
+ )
38
+}
39
+
40
+commit_file () {
41
+ test_tick &&
42
+ git commit "$@" -m "Commit $*" >/dev/null
43
+}
44
+
45
+test_expect_success 'setup repository' '
46
+ test_create_repo sm1 &&
47
+ add_file . foo &&
48
+ head1=$(add_file sm1 foo1 foo2) &&
49
+ fullhead1=$(git -C sm1 rev-parse --verify HEAD)
50
+'
51
+
52
+test_expect_success 'added submodule' '
53
+ git add sm1 &&
54
+ git diff-index -p --submodule=diff HEAD >actual &&
55
+ cat >expected <<-EOF &&
56
+ Submodule sm1 0000000...$head1 (new submodule)
57
+ diff --git a/sm1/foo1 b/sm1/foo1
58
+ new file mode 100644
59
+ index 0000000..1715acd
60
+ --- /dev/null
61
+ +++ b/sm1/foo1
62
+ @@ -0,0 +1 @@
63
+ +foo1
64
+ diff --git a/sm1/foo2 b/sm1/foo2
65
+ new file mode 100644
66
+ index 0000000..54b060e
67
+ --- /dev/null
68
+ +++ b/sm1/foo2
69
+ @@ -0,0 +1 @@
70
+ +foo2
71
+ EOF
72
+ test_cmp expected actual
73
+'
74
+
75
+test_expect_success 'added submodule, set diff.submodule' '
76
+ test_config diff.submodule log &&
77
+ git add sm1 &&
78
+ git diff-index -p --submodule=diff HEAD >actual &&
79
+ cat >expected <<-EOF &&
80
+ Submodule sm1 0000000...$head1 (new submodule)
81
+ diff --git a/sm1/foo1 b/sm1/foo1
82
+ new file mode 100644
83
+ index 0000000..1715acd
84
+ --- /dev/null
85
+ +++ b/sm1/foo1
86
+ @@ -0,0 +1 @@
87
+ +foo1
88
+ diff --git a/sm1/foo2 b/sm1/foo2
89
+ new file mode 100644
90
+ index 0000000..54b060e
91
+ --- /dev/null
92
+ +++ b/sm1/foo2
93
+ @@ -0,0 +1 @@
94
+ +foo2
95
+ EOF
96
+ test_cmp expected actual
97
+'
98
+
99
+test_expect_success '--submodule=short overrides diff.submodule' '
100
+ test_config diff.submodule log &&
101
+ git add sm1 &&
102
+ git diff --submodule=short --cached >actual &&
103
+ cat >expected <<-EOF &&
104
+ diff --git a/sm1 b/sm1
105
+ new file mode 160000
106
+ index 0000000..$head1
107
+ --- /dev/null
108
+ +++ b/sm1
109
+ @@ -0,0 +1 @@
110
+ +Subproject commit $fullhead1
111
+ EOF
112
+ test_cmp expected actual
113
+'
114
+
115
+test_expect_success 'diff.submodule does not affect plumbing' '
116
+ test_config diff.submodule log &&
117
+ git diff-index -p HEAD >actual &&
118
+ cat >expected <<-EOF &&
119
+ diff --git a/sm1 b/sm1
120
+ new file mode 160000
121
+ index 0000000..$head1
122
+ --- /dev/null
123
+ +++ b/sm1
124
+ @@ -0,0 +1 @@
125
+ +Subproject commit $fullhead1
126
+ EOF
127
+ test_cmp expected actual
128
+'
129
+
130
+commit_file sm1 &&
131
+head2=$(add_file sm1 foo3)
132
+
133
+test_expect_success 'modified submodule(forward)' '
134
+ git diff-index -p --submodule=diff HEAD >actual &&
135
+ cat >expected <<-EOF &&
136
+ Submodule sm1 $head1..$head2:
137
+ diff --git a/sm1/foo3 b/sm1/foo3
138
+ new file mode 100644
139
+ index 0000000..c1ec6c6
140
+ --- /dev/null
141
+ +++ b/sm1/foo3
142
+ @@ -0,0 +1 @@
143
+ +foo3
144
+ EOF
145
+ test_cmp expected actual
146
+'
147
+
148
+test_expect_success 'modified submodule(forward)' '
149
+ git diff --submodule=diff >actual &&
150
+ cat >expected <<-EOF &&
151
+ Submodule sm1 $head1..$head2:
152
+ diff --git a/sm1/foo3 b/sm1/foo3
153
+ new file mode 100644
154
+ index 0000000..c1ec6c6
155
+ --- /dev/null
156
+ +++ b/sm1/foo3
157
+ @@ -0,0 +1 @@
158
+ +foo3
159
+ EOF
160
+ test_cmp expected actual
161
+'
162
+
163
+test_expect_success 'modified submodule(forward) --submodule' '
164
+ git diff --submodule >actual &&
165
+ cat >expected <<-EOF &&
166
+ Submodule sm1 $head1..$head2:
167
+ > Add foo3 ($added foo3)
168
+ EOF
169
+ test_cmp expected actual
170
+'
171
+
172
+fullhead2=$(cd sm1; git rev-parse --verify HEAD)
173
+test_expect_success 'modified submodule(forward) --submodule=short' '
174
+ git diff --submodule=short >actual &&
175
+ cat >expected <<-EOF &&
176
+ diff --git a/sm1 b/sm1
177
+ index $head1..$head2 160000
178
+ --- a/sm1
179
+ +++ b/sm1
180
+ @@ -1 +1 @@
181
+ -Subproject commit $fullhead1
182
+ +Subproject commit $fullhead2
183
+ EOF
184
+ test_cmp expected actual
185
+'
186
+
187
+commit_file sm1 &&
188
+head3=$(
189
+ cd sm1 &&
190
+ git reset --hard HEAD~2 >/dev/null &&
191
+ git rev-parse --short --verify HEAD
192
+)
193
+
194
+test_expect_success 'modified submodule(backward)' '
195
+ git diff-index -p --submodule=diff HEAD >actual &&
196
+ cat >expected <<-EOF &&
197
+ Submodule sm1 $head2..$head3 (rewind):
198
+ diff --git a/sm1/foo2 b/sm1/foo2
199
+ deleted file mode 100644
200
+ index 54b060e..0000000
201
+ --- a/sm1/foo2
202
+ +++ /dev/null
203
+ @@ -1 +0,0 @@
204
+ -foo2
205
+ diff --git a/sm1/foo3 b/sm1/foo3
206
+ deleted file mode 100644
207
+ index c1ec6c6..0000000
208
+ --- a/sm1/foo3
209
+ +++ /dev/null
210
+ @@ -1 +0,0 @@
211
+ -foo3
212
+ EOF
213
+ test_cmp expected actual
214
+'
215
+
216
+head4=$(add_file sm1 foo4 foo5)
217
+test_expect_success 'modified submodule(backward and forward)' '
218
+ git diff-index -p --submodule=diff HEAD >actual &&
219
+ cat >expected <<-EOF &&
220
+ Submodule sm1 $head2...$head4:
221
+ diff --git a/sm1/foo2 b/sm1/foo2
222
+ deleted file mode 100644
223
+ index 54b060e..0000000
224
+ --- a/sm1/foo2
225
+ +++ /dev/null
226
+ @@ -1 +0,0 @@
227
+ -foo2
228
+ diff --git a/sm1/foo3 b/sm1/foo3
229
+ deleted file mode 100644
230
+ index c1ec6c6..0000000
231
+ --- a/sm1/foo3
232
+ +++ /dev/null
233
+ @@ -1 +0,0 @@
234
+ -foo3
235
+ diff --git a/sm1/foo4 b/sm1/foo4
236
+ new file mode 100644
237
+ index 0000000..a0016db
238
+ --- /dev/null
239
+ +++ b/sm1/foo4
240
+ @@ -0,0 +1 @@
241
+ +foo4
242
+ diff --git a/sm1/foo5 b/sm1/foo5
243
+ new file mode 100644
244
+ index 0000000..d6f2413
245
+ --- /dev/null
246
+ +++ b/sm1/foo5
247
+ @@ -0,0 +1 @@
248
+ +foo5
249
+ EOF
250
+ test_cmp expected actual
251
+'
252
+
253
+commit_file sm1 &&
254
+mv sm1 sm1-bak &&
255
+echo sm1 >sm1 &&
256
+head5=$(git hash-object sm1 | cut -c1-7) &&
257
+git add sm1 &&
258
+rm -f sm1 &&
259
+mv sm1-bak sm1
260
+
261
+test_expect_success 'typechanged submodule(submodule->blob), --cached' '
262
+ git diff --submodule=diff --cached >actual &&
263
+ cat >expected <<-EOF &&
264
+ Submodule sm1 $head4...0000000 (submodule deleted)
265
+ diff --git a/sm1/foo1 b/sm1/foo1
266
+ deleted file mode 100644
267
+ index 1715acd..0000000
268
+ --- a/sm1/foo1
269
+ +++ /dev/null
270
+ @@ -1 +0,0 @@
271
+ -foo1
272
+ diff --git a/sm1/foo4 b/sm1/foo4
273
+ deleted file mode 100644
274
+ index a0016db..0000000
275
+ --- a/sm1/foo4
276
+ +++ /dev/null
277
+ @@ -1 +0,0 @@
278
+ -foo4
279
+ diff --git a/sm1/foo5 b/sm1/foo5
280
+ deleted file mode 100644
281
+ index d6f2413..0000000
282
+ --- a/sm1/foo5
283
+ +++ /dev/null
284
+ @@ -1 +0,0 @@
285
+ -foo5
286
+ diff --git a/sm1 b/sm1
287
+ new file mode 100644
288
+ index 0000000..9da5fb8
289
+ --- /dev/null
290
+ +++ b/sm1
291
+ @@ -0,0 +1 @@
292
+ +sm1
293
+ EOF
294
+ test_cmp expected actual
295
+'
296
+
297
+test_expect_success 'typechanged submodule(submodule->blob)' '
298
+ git diff --submodule=diff >actual &&
299
+ cat >expected <<-EOF &&
300
+ diff --git a/sm1 b/sm1
301
+ deleted file mode 100644
302
+ index 9da5fb8..0000000
303
+ --- a/sm1
304
+ +++ /dev/null
305
+ @@ -1 +0,0 @@
306
+ -sm1
307
+ Submodule sm1 0000000...$head4 (new submodule)
308
+ diff --git a/sm1/foo1 b/sm1/foo1
309
+ new file mode 100644
310
+ index 0000000..1715acd
311
+ --- /dev/null
312
+ +++ b/sm1/foo1
313
+ @@ -0,0 +1 @@
314
+ +foo1
315
+ diff --git a/sm1/foo4 b/sm1/foo4
316
+ new file mode 100644
317
+ index 0000000..a0016db
318
+ --- /dev/null
319
+ +++ b/sm1/foo4
320
+ @@ -0,0 +1 @@
321
+ +foo4
322
+ diff --git a/sm1/foo5 b/sm1/foo5
323
+ new file mode 100644
324
+ index 0000000..d6f2413
325
+ --- /dev/null
326
+ +++ b/sm1/foo5
327
+ @@ -0,0 +1 @@
328
+ +foo5
329
+ EOF
330
+ test_cmp expected actual
331
+'
332
+
333
+rm -rf sm1 &&
334
+git checkout-index sm1
335
+test_expect_success 'typechanged submodule(submodule->blob)' '
336
+ git diff-index -p --submodule=diff HEAD >actual &&
337
+ cat >expected <<-EOF &&
338
+ Submodule sm1 $head4...0000000 (submodule deleted)
339
+ diff --git a/sm1 b/sm1
340
+ new file mode 100644
341
+ index 0000000..9da5fb8
342
+ --- /dev/null
343
+ +++ b/sm1
344
+ @@ -0,0 +1 @@
345
+ +sm1
346
+ EOF
347
+ test_cmp expected actual
348
+'
349
+
350
+rm -f sm1 &&
351
+test_create_repo sm1 &&
352
+head6=$(add_file sm1 foo6 foo7)
353
+fullhead6=$(cd sm1; git rev-parse --verify HEAD)
354
+test_expect_success 'nonexistent commit' '
355
+ git diff-index -p --submodule=diff HEAD >actual &&
356
+ cat >expected <<-EOF &&
357
+ Submodule sm1 $head4...$head6 (commits not present)
358
+ EOF
359
+ test_cmp expected actual
360
+'
361
+
362
+commit_file
363
+test_expect_success 'typechanged submodule(blob->submodule)' '
364
+ git diff-index -p --submodule=diff HEAD >actual &&
365
+ cat >expected <<-EOF &&
366
+ diff --git a/sm1 b/sm1
367
+ deleted file mode 100644
368
+ index 9da5fb8..0000000
369
+ --- a/sm1
370
+ +++ /dev/null
371
+ @@ -1 +0,0 @@
372
+ -sm1
373
+ Submodule sm1 0000000...$head6 (new submodule)
374
+ diff --git a/sm1/foo6 b/sm1/foo6
375
+ new file mode 100644
376
+ index 0000000..462398b
377
+ --- /dev/null
378
+ +++ b/sm1/foo6
379
+ @@ -0,0 +1 @@
380
+ +foo6
381
+ diff --git a/sm1/foo7 b/sm1/foo7
382
+ new file mode 100644
383
+ index 0000000..6e9262c
384
+ --- /dev/null
385
+ +++ b/sm1/foo7
386
+ @@ -0,0 +1 @@
387
+ +foo7
388
+ EOF
389
+ test_cmp expected actual
390
+'
391
+
392
+commit_file sm1 &&
393
+test_expect_success 'submodule is up to date' '
394
+ git diff-index -p --submodule=diff HEAD >actual &&
395
+ cat >expected <<-EOF &&
396
+ EOF
397
+ test_cmp expected actual
398
+'
399
+
400
+test_expect_success 'submodule contains untracked content' '
401
+ echo new > sm1/new-file &&
402
+ git diff-index -p --submodule=diff HEAD >actual &&
403
+ cat >expected <<-EOF &&
404
+ Submodule sm1 contains untracked content
405
+ EOF
406
+ test_cmp expected actual
407
+'
408
+
409
+test_expect_success 'submodule contains untracked content (untracked ignored)' '
410
+ git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
411
+ ! test -s actual
412
+'
413
+
414
+test_expect_success 'submodule contains untracked content (dirty ignored)' '
415
+ git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual &&
416
+ ! test -s actual
417
+'
418
+
419
+test_expect_success 'submodule contains untracked content (all ignored)' '
420
+ git diff-index -p --ignore-submodules=all --submodule=diff HEAD >actual &&
421
+ ! test -s actual
422
+'
423
+
424
+test_expect_success 'submodule contains untracked and modified content' '
425
+ echo new > sm1/foo6 &&
426
+ git diff-index -p --submodule=diff HEAD >actual &&
427
+ cat >expected <<-EOF &&
428
+ Submodule sm1 contains untracked content
429
+ Submodule sm1 contains modified content
430
+ diff --git a/sm1/foo6 b/sm1/foo6
431
+ index 462398b..3e75765 100644
432
+ --- a/sm1/foo6
433
+ +++ b/sm1/foo6
434
+ @@ -1 +1 @@
435
+ -foo6
436
+ +new
437
+ EOF
438
+ test_cmp expected actual
439
+'
440
+
441
+# NOT OK
442
+test_expect_success 'submodule contains untracked and modified content (untracked ignored)' '
443
+ echo new > sm1/foo6 &&
444
+ git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
445
+ cat >expected <<-EOF &&
446
+ Submodule sm1 contains modified content
447
+ diff --git a/sm1/foo6 b/sm1/foo6
448
+ index 462398b..3e75765 100644
449
+ --- a/sm1/foo6
450
+ +++ b/sm1/foo6
451
+ @@ -1 +1 @@
452
+ -foo6
453
+ +new
454
+ EOF
455
+ test_cmp expected actual
456
+'
457
+
458
+test_expect_success 'submodule contains untracked and modified content (dirty ignored)' '
459
+ echo new > sm1/foo6 &&
460
+ git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual &&
461
+ ! test -s actual
462
+'
463
+
464
+test_expect_success 'submodule contains untracked and modified content (all ignored)' '
465
+ echo new > sm1/foo6 &&
466
+ git diff-index -p --ignore-submodules --submodule=diff HEAD >actual &&
467
+ ! test -s actual
468
+'
469
+
470
+test_expect_success 'submodule contains modified content' '
471
+ rm -f sm1/new-file &&
472
+ git diff-index -p --submodule=diff HEAD >actual &&
473
+ cat >expected <<-EOF &&
474
+ Submodule sm1 contains modified content
475
+ diff --git a/sm1/foo6 b/sm1/foo6
476
+ index 462398b..3e75765 100644
477
+ --- a/sm1/foo6
478
+ +++ b/sm1/foo6
479
+ @@ -1 +1 @@
480
+ -foo6
481
+ +new
482
+ EOF
483
+ test_cmp expected actual
484
+'
485
+
486
+(cd sm1; git commit -mchange foo6 >/dev/null) &&
487
+head8=$(cd sm1; git rev-parse --short --verify HEAD) &&
488
+test_expect_success 'submodule is modified' '
489
+ git diff-index -p --submodule=diff HEAD >actual &&
490
+ cat >expected <<-EOF &&
491
+ Submodule sm1 17243c9..$head8:
492
+ diff --git a/sm1/foo6 b/sm1/foo6
493
+ index 462398b..3e75765 100644
494
+ --- a/sm1/foo6
495
+ +++ b/sm1/foo6
496
+ @@ -1 +1 @@
497
+ -foo6
498
+ +new
499
+ EOF
500
+ test_cmp expected actual
501
+'
502
+
503
+test_expect_success 'modified submodule contains untracked content' '
504
+ echo new > sm1/new-file &&
505
+ git diff-index -p --submodule=diff HEAD >actual &&
506
+ cat >expected <<-EOF &&
507
+ Submodule sm1 contains untracked content
508
+ Submodule sm1 17243c9..$head8:
509
+ diff --git a/sm1/foo6 b/sm1/foo6
510
+ index 462398b..3e75765 100644
511
+ --- a/sm1/foo6
512
+ +++ b/sm1/foo6
513
+ @@ -1 +1 @@
514
+ -foo6
515
+ +new
516
+ EOF
517
+ test_cmp expected actual
518
+'
519
+
520
+test_expect_success 'modified submodule contains untracked content (untracked ignored)' '
521
+ git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
522
+ cat >expected <<-EOF &&
523
+ Submodule sm1 17243c9..$head8:
524
+ diff --git a/sm1/foo6 b/sm1/foo6
525
+ index 462398b..3e75765 100644
526
+ --- a/sm1/foo6
527
+ +++ b/sm1/foo6
528
+ @@ -1 +1 @@
529
+ -foo6
530
+ +new
531
+ EOF
532
+ test_cmp expected actual
533
+'
534
+
535
+test_expect_success 'modified submodule contains untracked content (dirty ignored)' '
536
+ git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual &&
537
+ cat >expected <<-EOF &&
538
+ Submodule sm1 17243c9..cfce562:
539
+ diff --git a/sm1/foo6 b/sm1/foo6
540
+ index 462398b..3e75765 100644
541
+ --- a/sm1/foo6
542
+ +++ b/sm1/foo6
543
+ @@ -1 +1 @@
544
+ -foo6
545
+ +new
546
+ EOF
547
+ test_cmp expected actual
548
+'
549
+
550
+test_expect_success 'modified submodule contains untracked content (all ignored)' '
551
+ git diff-index -p --ignore-submodules=all --submodule=diff HEAD >actual &&
552
+ ! test -s actual
553
+'
554
+
555
+test_expect_success 'modified submodule contains untracked and modified content' '
556
+ echo modification >> sm1/foo6 &&
557
+ git diff-index -p --submodule=diff HEAD >actual &&
558
+ cat >expected <<-EOF &&
559
+ Submodule sm1 contains untracked content
560
+ Submodule sm1 contains modified content
561
+ Submodule sm1 17243c9..cfce562:
562
+ diff --git a/sm1/foo6 b/sm1/foo6
563
+ index 462398b..dfda541 100644
564
+ --- a/sm1/foo6
565
+ +++ b/sm1/foo6
566
+ @@ -1 +1,2 @@
567
+ -foo6
568
+ +new
569
+ +modification
570
+ EOF
571
+ test_cmp expected actual
572
+'
573
+
574
+test_expect_success 'modified submodule contains untracked and modified content (untracked ignored)' '
575
+ echo modification >> sm1/foo6 &&
576
+ git diff-index -p --ignore-submodules=untracked --submodule=diff HEAD >actual &&
577
+ cat >expected <<-EOF &&
578
+ Submodule sm1 contains modified content
579
+ Submodule sm1 17243c9..cfce562:
580
+ diff --git a/sm1/foo6 b/sm1/foo6
581
+ index 462398b..e20e2d9 100644
582
+ --- a/sm1/foo6
583
+ +++ b/sm1/foo6
584
+ @@ -1 +1,3 @@
585
+ -foo6
586
+ +new
587
+ +modification
588
+ +modification
589
+ EOF
590
+ test_cmp expected actual
591
+'
592
+
593
+test_expect_success 'modified submodule contains untracked and modified content (dirty ignored)' '
594
+ echo modification >> sm1/foo6 &&
595
+ git diff-index -p --ignore-submodules=dirty --submodule=diff HEAD >actual &&
596
+ cat >expected <<-EOF &&
597
+ Submodule sm1 17243c9..cfce562:
598
+ diff --git a/sm1/foo6 b/sm1/foo6
599
+ index 462398b..3e75765 100644
600
+ --- a/sm1/foo6
601
+ +++ b/sm1/foo6
602
+ @@ -1 +1 @@
603
+ -foo6
604
+ +new
605
+ EOF
606
+ test_cmp expected actual
607
+'
608
+
609
+test_expect_success 'modified submodule contains untracked and modified content (all ignored)' '
610
+ echo modification >> sm1/foo6 &&
611
+ git diff-index -p --ignore-submodules --submodule=diff HEAD >actual &&
612
+ ! test -s actual
613
+'
614
+
615
+# NOT OK
616
+test_expect_success 'modified submodule contains modified content' '
617
+ rm -f sm1/new-file &&
618
+ git diff-index -p --submodule=diff HEAD >actual &&
619
+ cat >expected <<-EOF &&
620
+ Submodule sm1 contains modified content
621
+ Submodule sm1 17243c9..cfce562:
622
+ diff --git a/sm1/foo6 b/sm1/foo6
623
+ index 462398b..ac466ca 100644
624
+ --- a/sm1/foo6
625
+ +++ b/sm1/foo6
626
+ @@ -1 +1,5 @@
627
+ -foo6
628
+ +new
629
+ +modification
630
+ +modification
631
+ +modification
632
+ +modification
633
+ EOF
634
+ test_cmp expected actual
635
+'
636
+
637
+rm -rf sm1
638
+test_expect_success 'deleted submodule' '
639
+ git diff-index -p --submodule=diff HEAD >actual &&
640
+ cat >expected <<-EOF &&
641
+ Submodule sm1 17243c9...0000000 (submodule deleted)
642
+ EOF
643
+ test_cmp expected actual
644
+'
645
+
646
+test_create_repo sm2 &&
647
+head7=$(add_file sm2 foo8 foo9) &&
648
+git add sm2
649
+
650
+test_expect_success 'multiple submodules' '
651
+ git diff-index -p --submodule=diff HEAD >actual &&
652
+ cat >expected <<-EOF &&
653
+ Submodule sm1 17243c9...0000000 (submodule deleted)
654
+ Submodule sm2 0000000...a5a65c9 (new submodule)
655
+ diff --git a/sm2/foo8 b/sm2/foo8
656
+ new file mode 100644
657
+ index 0000000..db9916b
658
+ --- /dev/null
659
+ +++ b/sm2/foo8
660
+ @@ -0,0 +1 @@
661
+ +foo8
662
+ diff --git a/sm2/foo9 b/sm2/foo9
663
+ new file mode 100644
664
+ index 0000000..9c3b4f6
665
+ --- /dev/null
666
+ +++ b/sm2/foo9
667
+ @@ -0,0 +1 @@
668
+ +foo9
669
+ EOF
670
+ test_cmp expected actual
671
+'
672
+
673
+test_expect_success 'path filter' '
674
+ git diff-index -p --submodule=diff HEAD sm2 >actual &&
675
+ cat >expected <<-EOF &&
676
+ Submodule sm2 0000000...a5a65c9 (new submodule)
677
+ diff --git a/sm2/foo8 b/sm2/foo8
678
+ new file mode 100644
679
+ index 0000000..db9916b
680
+ --- /dev/null
681
+ +++ b/sm2/foo8
682
+ @@ -0,0 +1 @@
683
+ +foo8
684
+ diff --git a/sm2/foo9 b/sm2/foo9
685
+ new file mode 100644
686
+ index 0000000..9c3b4f6
687
+ --- /dev/null
688
+ +++ b/sm2/foo9
689
+ @@ -0,0 +1 @@
690
+ +foo9
691
+ EOF
692
+ test_cmp expected actual
693
+'
694
+
695
+commit_file sm2
696
+test_expect_success 'given commit' '
697
+ git diff-index -p --submodule=diff HEAD^ >actual &&
698
+ cat >expected <<-EOF &&
699
+ Submodule sm1 17243c9...0000000 (submodule deleted)
700
+ Submodule sm2 0000000...a5a65c9 (new submodule)
701
+ diff --git a/sm2/foo8 b/sm2/foo8
702
+ new file mode 100644
703
+ index 0000000..db9916b
704
+ --- /dev/null
705
+ +++ b/sm2/foo8
706
+ @@ -0,0 +1 @@
707
+ +foo8
708
+ diff --git a/sm2/foo9 b/sm2/foo9
709
+ new file mode 100644
710
+ index 0000000..9c3b4f6
711
+ --- /dev/null
712
+ +++ b/sm2/foo9
713
+ @@ -0,0 +1 @@
714
+ +foo9
715
+ EOF
716
+ test_cmp expected actual
717
+'
718
+
719
+test_expect_success 'setup .git file for sm2' '
720
+ (cd sm2 &&
721
+ REAL="$(pwd)/../.real" &&
722
+ mv .git "$REAL"
723
+ echo "gitdir: $REAL" >.git)
724
+'
725
+
726
+test_expect_success 'diff --submodule=diff with .git file' '
727
+ git diff --submodule=diff HEAD^ >actual &&
728
+ cat >expected <<-EOF &&
729
+ Submodule sm1 17243c9...0000000 (submodule deleted)
730
+ Submodule sm2 0000000...a5a65c9 (new submodule)
731
+ diff --git a/sm2/foo8 b/sm2/foo8
732
+ new file mode 100644
733
+ index 0000000..db9916b
734
+ --- /dev/null
735
+ +++ b/sm2/foo8
736
+ @@ -0,0 +1 @@
737
+ +foo8
738
+ diff --git a/sm2/foo9 b/sm2/foo9
739
+ new file mode 100644
740
+ index 0000000..9c3b4f6
741
+ --- /dev/null
742
+ +++ b/sm2/foo9
743
+ @@ -0,0 +1 @@
744
+ +foo9
745
+ EOF
746
+ test_cmp expected actual
747
+'
748
+
749
+test_done