Merge branch 'jk/log-cherry-pick-duplicate-patches'
[git] / t / t4211-line-log.sh
1 #!/bin/sh
2
3 test_description='test log -L'
4 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
5 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
6
7 . ./test-lib.sh
8
9 test_expect_success 'setup (import history)' '
10         git fast-import < "$TEST_DIRECTORY"/t4211/history.export &&
11         git reset --hard
12 '
13
14 test_expect_success 'basic command line parsing' '
15         # This may fail due to "no such path a.c in commit", or
16         # "-L is incompatible with pathspec", depending on the
17         # order the error is checked.  Either is acceptable.
18         test_must_fail git log -L1,1:a.c -- a.c &&
19
20         # -L requires there is no pathspec
21         test_must_fail git log -L1,1:b.c -- b.c 2>error &&
22         test_i18ngrep "cannot be used with pathspec" error &&
23
24         # This would fail because --follow wants a single path, but
25         # we may fail due to incompatibility between -L/--follow in
26         # the future.  Either is acceptable.
27         test_must_fail git log -L1,1:b.c --follow &&
28         test_must_fail git log --follow -L1,1:b.c &&
29
30         # This would fail because -L wants no pathspec, but
31         # we may fail due to incompatibility between -L/--follow in
32         # the future.  Either is acceptable.
33         test_must_fail git log --follow -L1,1:b.c -- b.c
34 '
35
36 canned_test_1 () {
37         test_expect_$1 "$2" "
38                 git log $2 >actual &&
39                 test_cmp \"\$TEST_DIRECTORY\"/t4211/$(test_oid algo)/expect.$3 actual
40         "
41 }
42
43 canned_test () {
44         canned_test_1 success "$@"
45 }
46 canned_test_failure () {
47         canned_test_1 failure "$@"
48 }
49
50 test_bad_opts () {
51         test_expect_success "invalid args: $1" "
52                 test_must_fail git log $1 2>errors &&
53                 test_i18ngrep '$2' errors
54         "
55 }
56
57 canned_test "-L 4,12:a.c simple" simple-f
58 canned_test "-L 4,+9:a.c simple" simple-f
59 canned_test "-L '/long f/,/^}/:a.c' simple" simple-f
60 canned_test "-L :f:a.c simple" simple-f-to-main
61
62 canned_test "-L '/main/,/^}/:a.c' simple" simple-main
63 canned_test "-L :main:a.c simple" simple-main-to-end
64
65 canned_test "-L 1,+4:a.c simple" beginning-of-file
66
67 canned_test "-L 20:a.c simple" end-of-file
68
69 canned_test "-L '/long f/',/^}/:a.c -L /main/,/^}/:a.c simple" two-ranges
70 canned_test "-L 24,+1:a.c simple" vanishes-early
71
72 canned_test "-M -L '/long f/,/^}/:b.c' move-support" move-support-f
73 canned_test "-M -L ':f:b.c' parallel-change" parallel-change-f-to-main
74
75 canned_test "-L 4,12:a.c -L :main:a.c simple" multiple
76 canned_test "-L 4,18:a.c -L ^:main:a.c simple" multiple-overlapping
77 canned_test "-L :main:a.c -L 4,18:a.c simple" multiple-overlapping
78 canned_test "-L 4:a.c -L 8,12:a.c simple" multiple-superset
79 canned_test "-L 8,12:a.c -L 4:a.c simple" multiple-superset
80
81 test_bad_opts "-L" "switch.*requires a value"
82 test_bad_opts "-L b.c" "argument not .start,end:file"
83 test_bad_opts "-L 1:" "argument not .start,end:file"
84 test_bad_opts "-L 1:nonexistent" "There is no path"
85 test_bad_opts "-L 1:simple" "There is no path"
86 test_bad_opts "-L '/foo:b.c'" "argument not .start,end:file"
87 test_bad_opts "-L 1000:b.c" "has only.*lines"
88 test_bad_opts "-L :b.c" "argument not .start,end:file"
89 test_bad_opts "-L :foo:b.c" "no match"
90
91 test_expect_success '-L X (X == nlines)' '
92         n=$(wc -l <b.c) &&
93         git log -L $n:b.c
94 '
95
96 test_expect_success '-L X (X == nlines + 1)' '
97         n=$(expr $(wc -l <b.c) + 1) &&
98         test_must_fail git log -L $n:b.c
99 '
100
101 test_expect_success '-L X (X == nlines + 2)' '
102         n=$(expr $(wc -l <b.c) + 2) &&
103         test_must_fail git log -L $n:b.c
104 '
105
106 test_expect_success '-L ,Y (Y == nlines)' '
107         n=$(printf "%d" $(wc -l <b.c)) &&
108         git log -L ,$n:b.c
109 '
110
111 test_expect_success '-L ,Y (Y == nlines + 1)' '
112         n=$(expr $(wc -l <b.c) + 1) &&
113         git log -L ,$n:b.c
114 '
115
116 test_expect_success '-L ,Y (Y == nlines + 2)' '
117         n=$(expr $(wc -l <b.c) + 2) &&
118         git log -L ,$n:b.c
119 '
120
121 test_expect_success '-L with --first-parent and a merge' '
122         git checkout parallel-change &&
123         git log --first-parent -L 1,1:b.c
124 '
125
126 test_expect_success '-L with --output' '
127         git checkout parallel-change &&
128         git log --output=log -L :main:b.c >output &&
129         test_must_be_empty output &&
130         test_line_count = 70 log
131 '
132
133 test_expect_success 'range_set_union' '
134         test_seq 500 > c.c &&
135         git add c.c &&
136         git commit -m "many lines" &&
137         test_seq 1000 > c.c &&
138         git add c.c &&
139         git commit -m "modify many lines" &&
140         git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c; done)
141 '
142
143 test_expect_success '-s shows only line-log commits' '
144         git log --format="commit %s" -L1,24:b.c >expect.raw &&
145         grep ^commit expect.raw >expect &&
146         git log --format="commit %s" -L1,24:b.c -s >actual &&
147         test_cmp expect actual
148 '
149
150 test_expect_success '-p shows the default patch output' '
151         git log -L1,24:b.c >expect &&
152         git log -L1,24:b.c -p >actual &&
153         test_cmp expect actual
154 '
155
156 test_expect_success '--raw is forbidden' '
157         test_must_fail git log -L1,24:b.c --raw
158 '
159
160 test_expect_success 'setup for checking fancy rename following' '
161         git checkout --orphan moves-start &&
162         git reset --hard &&
163
164         printf "%s\n"    12 13 14 15      b c d e   >file-1 &&
165         printf "%s\n"    22 23 24 25      B C D E   >file-2 &&
166         git add file-1 file-2 &&
167         test_tick &&
168         git commit -m "Add file-1 and file-2" &&
169         oid_add_f1_f2=$(git rev-parse --short HEAD) &&
170
171         git checkout -b moves-main &&
172         printf "%s\n" 11 12 13 14 15      b c d e   >file-1 &&
173         git commit -a -m "Modify file-1 on main" &&
174         oid_mod_f1_main=$(git rev-parse --short HEAD) &&
175
176         printf "%s\n" 21 22 23 24 25      B C D E   >file-2 &&
177         git commit -a -m "Modify file-2 on main #1" &&
178         oid_mod_f2_main_1=$(git rev-parse --short HEAD) &&
179
180         git mv file-1 renamed-1 &&
181         git commit -m "Rename file-1 to renamed-1 on main" &&
182
183         printf "%s\n" 11 12 13 14 15      b c d e f >renamed-1 &&
184         git commit -a -m "Modify renamed-1 on main" &&
185         oid_mod_r1_main=$(git rev-parse --short HEAD) &&
186
187         printf "%s\n" 21 22 23 24 25      B C D E F >file-2 &&
188         git commit -a -m "Modify file-2 on main #2" &&
189         oid_mod_f2_main_2=$(git rev-parse --short HEAD) &&
190
191         git checkout -b moves-side moves-start &&
192         printf "%s\n"    12 13 14 15 16   b c d e   >file-1 &&
193         git commit -a -m "Modify file-1 on side #1" &&
194         oid_mod_f1_side_1=$(git rev-parse --short HEAD) &&
195
196         printf "%s\n"    22 23 24 25 26   B C D E   >file-2 &&
197         git commit -a -m "Modify file-2 on side" &&
198         oid_mod_f2_side=$(git rev-parse --short HEAD) &&
199
200         git mv file-2 renamed-2 &&
201         git commit -m "Rename file-2 to renamed-2 on side" &&
202
203         printf "%s\n"    12 13 14 15 16 a b c d e   >file-1 &&
204         git commit -a -m "Modify file-1 on side #2" &&
205         oid_mod_f1_side_2=$(git rev-parse --short HEAD) &&
206
207         printf "%s\n"    22 23 24 25 26 A B C D E   >renamed-2 &&
208         git commit -a -m "Modify renamed-2 on side" &&
209         oid_mod_r2_side=$(git rev-parse --short HEAD) &&
210
211         git checkout moves-main &&
212         git merge moves-side &&
213         oid_merge=$(git rev-parse --short HEAD)
214 '
215
216 test_expect_success 'fancy rename following #1' '
217         cat >expect <<-EOF &&
218         $oid_merge Merge branch '\''moves-side'\'' into moves-main
219         $oid_mod_f1_side_2 Modify file-1 on side #2
220         $oid_mod_f1_side_1 Modify file-1 on side #1
221         $oid_mod_r1_main Modify renamed-1 on main
222         $oid_mod_f1_main Modify file-1 on main
223         $oid_add_f1_f2 Add file-1 and file-2
224         EOF
225         git log -L1:renamed-1 --oneline --no-patch >actual &&
226         test_cmp expect actual
227 '
228
229 test_expect_success 'fancy rename following #2' '
230         cat >expect <<-EOF &&
231         $oid_merge Merge branch '\''moves-side'\'' into moves-main
232         $oid_mod_r2_side Modify renamed-2 on side
233         $oid_mod_f2_side Modify file-2 on side
234         $oid_mod_f2_main_2 Modify file-2 on main #2
235         $oid_mod_f2_main_1 Modify file-2 on main #1
236         $oid_add_f1_f2 Add file-1 and file-2
237         EOF
238         git log -L1:renamed-2 --oneline --no-patch >actual &&
239         test_cmp expect actual
240 '
241
242 # Create the following linear history, where each commit does what its
243 # subject line promises:
244 #
245 #   * 66c6410 Modify func2() in file.c
246 #   * 50834e5 Modify other-file
247 #   * fe5851c Modify func1() in file.c
248 #   * 8c7c7dd Add other-file
249 #   * d5f4417 Add func1() and func2() in file.c
250 test_expect_success 'setup for checking line-log and parent oids' '
251         git checkout --orphan parent-oids &&
252         git reset --hard &&
253
254         cat >file.c <<-\EOF &&
255         int func1()
256         {
257             return F1;
258         }
259
260         int func2()
261         {
262             return F2;
263         }
264         EOF
265         git add file.c &&
266         test_tick &&
267         first_tick=$test_tick &&
268         git commit -m "Add func1() and func2() in file.c" &&
269
270         echo 1 >other-file &&
271         git add other-file &&
272         test_tick &&
273         git commit -m "Add other-file" &&
274
275         sed -e "s/F1/F1 + 1/" file.c >tmp &&
276         mv tmp file.c &&
277         git commit -a -m "Modify func1() in file.c" &&
278
279         echo 2 >other-file &&
280         git commit -a -m "Modify other-file" &&
281
282         sed -e "s/F2/F2 + 2/" file.c >tmp &&
283         mv tmp file.c &&
284         git commit -a -m "Modify func2() in file.c" &&
285
286         head_oid=$(git rev-parse --short HEAD) &&
287         prev_oid=$(git rev-parse --short HEAD^) &&
288         root_oid=$(git rev-parse --short HEAD~4)
289 '
290
291 # Parent oid should be from immediate parent.
292 test_expect_success 'parent oids without parent rewriting' '
293         cat >expect <<-EOF &&
294         $head_oid $prev_oid Modify func2() in file.c
295         $root_oid  Add func1() and func2() in file.c
296         EOF
297         git log --format="%h %p %s" --no-patch -L:func2:file.c >actual &&
298         test_cmp expect actual
299 '
300
301 # Parent oid should be from the most recent ancestor touching func2(),
302 # i.e. in this case from the root commit.
303 test_expect_success 'parent oids with parent rewriting' '
304         cat >expect <<-EOF &&
305         $head_oid $root_oid Modify func2() in file.c
306         $root_oid  Add func1() and func2() in file.c
307         EOF
308         git log --format="%h %p %s" --no-patch -L:func2:file.c --parents >actual &&
309         test_cmp expect actual
310 '
311
312 test_expect_success 'line-log with --before' '
313         echo $root_oid >expect &&
314         git log --format=%h --no-patch -L:func2:file.c --before=$first_tick >actual &&
315         test_cmp expect actual
316 '
317
318 test_done