combined diff: correctly handle truncated file
[git] / t / t4014-format-patch.sh
1 #!/bin/sh
2 #
3 # Copyright (c) 2006 Junio C Hamano
4 #
5
6 test_description='various format-patch tests'
7
8 . ./test-lib.sh
9
10 test_expect_success setup '
11
12         for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file &&
13         cat file >elif &&
14         git add file elif &&
15         git commit -m Initial &&
16         git checkout -b side &&
17
18         for i in 1 2 5 6 A B C 7 8 9 10; do echo "$i"; done >file &&
19         test_chmod +x elif &&
20         git commit -m "Side changes #1" &&
21
22         for i in D E F; do echo "$i"; done >>file &&
23         git update-index file &&
24         git commit -m "Side changes #2" &&
25         git tag C2 &&
26
27         for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >file &&
28         git update-index file &&
29         git commit -m "Side changes #3 with \\n backslash-n in it." &&
30
31         git checkout master &&
32         git diff-tree -p C2 | git apply --index &&
33         git commit -m "Master accepts moral equivalent of #2"
34
35 '
36
37 test_expect_success "format-patch --ignore-if-in-upstream" '
38
39         git format-patch --stdout master..side >patch0 &&
40         cnt=`grep "^From " patch0 | wc -l` &&
41         test $cnt = 3
42
43 '
44
45 test_expect_success "format-patch --ignore-if-in-upstream" '
46
47         git format-patch --stdout \
48                 --ignore-if-in-upstream master..side >patch1 &&
49         cnt=`grep "^From " patch1 | wc -l` &&
50         test $cnt = 2
51
52 '
53
54 test_expect_success "format-patch result applies" '
55
56         git checkout -b rebuild-0 master &&
57         git am -3 patch0 &&
58         cnt=`git rev-list master.. | wc -l` &&
59         test $cnt = 2
60 '
61
62 test_expect_success "format-patch --ignore-if-in-upstream result applies" '
63
64         git checkout -b rebuild-1 master &&
65         git am -3 patch1 &&
66         cnt=`git rev-list master.. | wc -l` &&
67         test $cnt = 2
68 '
69
70 test_expect_success 'commit did not screw up the log message' '
71
72         git cat-file commit side | grep "^Side .* with .* backslash-n"
73
74 '
75
76 test_expect_success 'format-patch did not screw up the log message' '
77
78         grep "^Subject: .*Side changes #3 with .* backslash-n" patch0 &&
79         grep "^Subject: .*Side changes #3 with .* backslash-n" patch1
80
81 '
82
83 test_expect_success 'replay did not screw up the log message' '
84
85         git cat-file commit rebuild-1 | grep "^Side .* with .* backslash-n"
86
87 '
88
89 test_expect_success 'extra headers' '
90
91         git config format.headers "To: R. E. Cipient <rcipient@example.com>
92 " &&
93         git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>
94 " &&
95         git format-patch --stdout master..side > patch2 &&
96         sed -e "/^\$/q" patch2 > hdrs2 &&
97         grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs2 &&
98         grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs2
99
100 '
101
102 test_expect_success 'extra headers without newlines' '
103
104         git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
105         git config --add format.headers "Cc: S. E. Cipient <scipient@example.com>" &&
106         git format-patch --stdout master..side >patch3 &&
107         sed -e "/^\$/q" patch3 > hdrs3 &&
108         grep "^To: R. E. Cipient <rcipient@example.com>\$" hdrs3 &&
109         grep "^Cc: S. E. Cipient <scipient@example.com>\$" hdrs3
110
111 '
112
113 test_expect_success 'extra headers with multiple To:s' '
114
115         git config --replace-all format.headers "To: R. E. Cipient <rcipient@example.com>" &&
116         git config --add format.headers "To: S. E. Cipient <scipient@example.com>" &&
117         git format-patch --stdout master..side > patch4 &&
118         sed -e "/^\$/q" patch4 > hdrs4 &&
119         grep "^To: R. E. Cipient <rcipient@example.com>,\$" hdrs4 &&
120         grep "^ *S. E. Cipient <scipient@example.com>\$" hdrs4
121 '
122
123 test_expect_success 'additional command line cc' '
124
125         git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
126         git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
127         grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch5 &&
128         grep "^ *S. E. Cipient <scipient@example.com>\$" patch5
129 '
130
131 test_expect_success 'command line headers' '
132
133         git config --unset-all format.headers &&
134         git format-patch --add-header="Cc: R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
135         grep "^Cc: R. E. Cipient <rcipient@example.com>\$" patch6
136 '
137
138 test_expect_success 'configuration headers and command line headers' '
139
140         git config --replace-all format.headers "Cc: R. E. Cipient <rcipient@example.com>" &&
141         git format-patch --add-header="Cc: S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
142         grep "^Cc: R. E. Cipient <rcipient@example.com>,\$" patch7 &&
143         grep "^ *S. E. Cipient <scipient@example.com>\$" patch7
144 '
145
146 test_expect_success 'multiple files' '
147
148         rm -rf patches/ &&
149         git checkout side &&
150         git format-patch -o patches/ master &&
151         ls patches/0001-Side-changes-1.patch patches/0002-Side-changes-2.patch patches/0003-Side-changes-3-with-n-backslash-n-in-it.patch
152 '
153
154 check_threading () {
155         expect="$1" &&
156         shift &&
157         (git format-patch --stdout "$@"; echo $? > status.out) |
158         # Prints everything between the Message-ID and In-Reply-To,
159         # and replaces all Message-ID-lookalikes by a sequence number
160         perl -ne '
161                 if (/^(message-id|references|in-reply-to)/i) {
162                         $printing = 1;
163                 } elsif (/^\S/) {
164                         $printing = 0;
165                 }
166                 if ($printing) {
167                         $h{$1}=$i++ if (/<([^>]+)>/ and !exists $h{$1});
168                         for $k (keys %h) {s/$k/$h{$k}/};
169                         print;
170                 }
171                 print "---\n" if /^From /i;
172         ' > actual &&
173         test 0 = "$(cat status.out)" &&
174         test_cmp "$expect" actual
175 }
176
177 cat >> expect.no-threading <<EOF
178 ---
179 ---
180 ---
181 EOF
182
183 test_expect_success 'no threading' '
184         git checkout side &&
185         check_threading expect.no-threading master
186 '
187
188 cat > expect.thread <<EOF
189 ---
190 Message-Id: <0>
191 ---
192 Message-Id: <1>
193 In-Reply-To: <0>
194 References: <0>
195 ---
196 Message-Id: <2>
197 In-Reply-To: <0>
198 References: <0>
199 EOF
200
201 test_expect_success 'thread' '
202         check_threading expect.thread --thread master
203 '
204
205 cat > expect.in-reply-to <<EOF
206 ---
207 Message-Id: <0>
208 In-Reply-To: <1>
209 References: <1>
210 ---
211 Message-Id: <2>
212 In-Reply-To: <1>
213 References: <1>
214 ---
215 Message-Id: <3>
216 In-Reply-To: <1>
217 References: <1>
218 EOF
219
220 test_expect_success 'thread in-reply-to' '
221         check_threading expect.in-reply-to --in-reply-to="<test.message>" \
222                 --thread master
223 '
224
225 cat > expect.cover-letter <<EOF
226 ---
227 Message-Id: <0>
228 ---
229 Message-Id: <1>
230 In-Reply-To: <0>
231 References: <0>
232 ---
233 Message-Id: <2>
234 In-Reply-To: <0>
235 References: <0>
236 ---
237 Message-Id: <3>
238 In-Reply-To: <0>
239 References: <0>
240 EOF
241
242 test_expect_success 'thread cover-letter' '
243         check_threading expect.cover-letter --cover-letter --thread master
244 '
245
246 cat > expect.cl-irt <<EOF
247 ---
248 Message-Id: <0>
249 In-Reply-To: <1>
250 References: <1>
251 ---
252 Message-Id: <2>
253 In-Reply-To: <0>
254 References: <1>
255         <0>
256 ---
257 Message-Id: <3>
258 In-Reply-To: <0>
259 References: <1>
260         <0>
261 ---
262 Message-Id: <4>
263 In-Reply-To: <0>
264 References: <1>
265         <0>
266 EOF
267
268 test_expect_success 'thread cover-letter in-reply-to' '
269         check_threading expect.cl-irt --cover-letter \
270                 --in-reply-to="<test.message>" --thread master
271 '
272
273 test_expect_success 'thread explicit shallow' '
274         check_threading expect.cl-irt --cover-letter \
275                 --in-reply-to="<test.message>" --thread=shallow master
276 '
277
278 cat > expect.deep <<EOF
279 ---
280 Message-Id: <0>
281 ---
282 Message-Id: <1>
283 In-Reply-To: <0>
284 References: <0>
285 ---
286 Message-Id: <2>
287 In-Reply-To: <1>
288 References: <0>
289         <1>
290 EOF
291
292 test_expect_success 'thread deep' '
293         check_threading expect.deep --thread=deep master
294 '
295
296 cat > expect.deep-irt <<EOF
297 ---
298 Message-Id: <0>
299 In-Reply-To: <1>
300 References: <1>
301 ---
302 Message-Id: <2>
303 In-Reply-To: <0>
304 References: <1>
305         <0>
306 ---
307 Message-Id: <3>
308 In-Reply-To: <2>
309 References: <1>
310         <0>
311         <2>
312 EOF
313
314 test_expect_success 'thread deep in-reply-to' '
315         check_threading expect.deep-irt  --thread=deep \
316                 --in-reply-to="<test.message>" master
317 '
318
319 cat > expect.deep-cl <<EOF
320 ---
321 Message-Id: <0>
322 ---
323 Message-Id: <1>
324 In-Reply-To: <0>
325 References: <0>
326 ---
327 Message-Id: <2>
328 In-Reply-To: <1>
329 References: <0>
330         <1>
331 ---
332 Message-Id: <3>
333 In-Reply-To: <2>
334 References: <0>
335         <1>
336         <2>
337 EOF
338
339 test_expect_success 'thread deep cover-letter' '
340         check_threading expect.deep-cl --cover-letter --thread=deep master
341 '
342
343 cat > expect.deep-cl-irt <<EOF
344 ---
345 Message-Id: <0>
346 In-Reply-To: <1>
347 References: <1>
348 ---
349 Message-Id: <2>
350 In-Reply-To: <0>
351 References: <1>
352         <0>
353 ---
354 Message-Id: <3>
355 In-Reply-To: <2>
356 References: <1>
357         <0>
358         <2>
359 ---
360 Message-Id: <4>
361 In-Reply-To: <3>
362 References: <1>
363         <0>
364         <2>
365         <3>
366 EOF
367
368 test_expect_success 'thread deep cover-letter in-reply-to' '
369         check_threading expect.deep-cl-irt --cover-letter \
370                 --in-reply-to="<test.message>" --thread=deep master
371 '
372
373 test_expect_success 'thread via config' '
374         git config format.thread true &&
375         check_threading expect.thread master
376 '
377
378 test_expect_success 'thread deep via config' '
379         git config format.thread deep &&
380         check_threading expect.deep master
381 '
382
383 test_expect_success 'thread config + override' '
384         git config format.thread deep &&
385         check_threading expect.thread --thread master
386 '
387
388 test_expect_success 'thread config + --no-thread' '
389         git config format.thread deep &&
390         check_threading expect.no-threading --no-thread master
391 '
392
393 test_expect_success 'excessive subject' '
394
395         rm -rf patches/ &&
396         git checkout side &&
397         for i in 5 6 1 2 3 A 4 B C 7 8 9 10 D E F; do echo "$i"; done >>file &&
398         git update-index file &&
399         git commit -m "This is an excessively long subject line for a message due to the habit some projects have of not having a short, one-line subject at the start of the commit message, but rather sticking a whole paragraph right at the start as the only thing in the commit message. It had better not become the filename for the patch." &&
400         git format-patch -o patches/ master..side &&
401         ls patches/0004-This-is-an-excessively-long-subject-line-for-a-messa.patch
402 '
403
404 test_expect_success 'cover-letter inherits diff options' '
405
406         git mv file foo &&
407         git commit -m foo &&
408         git format-patch --cover-letter -1 &&
409         ! grep "file => foo .* 0 *\$" 0000-cover-letter.patch &&
410         git format-patch --cover-letter -1 -M &&
411         grep "file => foo .* 0 *\$" 0000-cover-letter.patch
412
413 '
414
415 cat > expect << EOF
416   This is an excessively long subject line for a message due to the
417     habit some projects have of not having a short, one-line subject at
418     the start of the commit message, but rather sticking a whole
419     paragraph right at the start as the only thing in the commit
420     message. It had better not become the filename for the patch.
421   foo
422
423 EOF
424
425 test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '
426
427         git format-patch --cover-letter -2 &&
428         sed -e "1,/A U Thor/d" -e "/^\$/q" < 0000-cover-letter.patch > output &&
429         test_cmp expect output
430
431 '
432
433 cat > expect << EOF
434 ---
435  file |   16 ++++++++++++++++
436  1 files changed, 16 insertions(+), 0 deletions(-)
437
438 diff --git a/file b/file
439 index 40f36c6..2dc5c23 100644
440 --- a/file
441 +++ b/file
442 @@ -13,4 +13,20 @@ C
443  10
444  D
445  E
446  F
447 +5
448 EOF
449
450 test_expect_success 'format-patch respects -U' '
451
452         git format-patch -U4 -2 &&
453         sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
454         test_cmp expect output
455
456 '
457
458 cat > expect << EOF
459
460 diff --git a/file b/file
461 index 40f36c6..2dc5c23 100644
462 --- a/file
463 +++ b/file
464 @@ -14,3 +14,19 @@ C
465  D
466  E
467  F
468 +5
469 EOF
470
471 test_expect_success 'format-patch -p suppresses stat' '
472
473         git format-patch -p -2 &&
474         sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
475         test_cmp expect output
476
477 '
478
479 test_expect_success 'format-patch from a subdirectory (1)' '
480         filename=$(
481                 rm -rf sub &&
482                 mkdir -p sub/dir &&
483                 cd sub/dir &&
484                 git format-patch -1
485         ) &&
486         case "$filename" in
487         0*)
488                 ;; # ok
489         *)
490                 echo "Oops? $filename"
491                 false
492                 ;;
493         esac &&
494         test -f "$filename"
495 '
496
497 test_expect_success 'format-patch from a subdirectory (2)' '
498         filename=$(
499                 rm -rf sub &&
500                 mkdir -p sub/dir &&
501                 cd sub/dir &&
502                 git format-patch -1 -o ..
503         ) &&
504         case "$filename" in
505         ../0*)
506                 ;; # ok
507         *)
508                 echo "Oops? $filename"
509                 false
510                 ;;
511         esac &&
512         basename=$(expr "$filename" : ".*/\(.*\)") &&
513         test -f "sub/$basename"
514 '
515
516 test_expect_success 'format-patch from a subdirectory (3)' '
517         rm -f 0* &&
518         filename=$(
519                 rm -rf sub &&
520                 mkdir -p sub/dir &&
521                 cd sub/dir &&
522                 git format-patch -1 -o "$TRASH_DIRECTORY"
523         ) &&
524         basename=$(expr "$filename" : ".*/\(.*\)") &&
525         test -f "$basename"
526 '
527
528 test_expect_success 'format-patch --in-reply-to' '
529         git format-patch -1 --stdout --in-reply-to "baz@foo.bar" > patch8 &&
530         grep "^In-Reply-To: <baz@foo.bar>" patch8 &&
531         grep "^References: <baz@foo.bar>" patch8
532 '
533
534 test_expect_success 'format-patch --signoff' '
535         git format-patch -1 --signoff --stdout |
536         grep "^Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
537 '
538
539 echo "fatal: --name-only does not make sense" > expect.name-only
540 echo "fatal: --name-status does not make sense" > expect.name-status
541 echo "fatal: --check does not make sense" > expect.check
542
543 test_expect_success 'options no longer allowed for format-patch' '
544         test_must_fail git format-patch --name-only 2> output &&
545         test_cmp expect.name-only output &&
546         test_must_fail git format-patch --name-status 2> output &&
547         test_cmp expect.name-status output &&
548         test_must_fail git format-patch --check 2> output &&
549         test_cmp expect.check output'
550
551 test_expect_success 'format-patch --numstat should produce a patch' '
552         git format-patch --numstat --stdout master..side > output &&
553         test 6 = $(grep "^diff --git a/" output | wc -l)'
554
555 test_expect_success 'format-patch -- <path>' '
556         git format-patch master..side -- file 2>error &&
557         ! grep "Use .--" error
558 '
559
560 test_expect_success 'format-patch --ignore-if-in-upstream HEAD' '
561         git format-patch --ignore-if-in-upstream HEAD
562 '
563
564 test_done