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