Merge branch 'pk/subsub-fetch-fix'
[git] / t / t8013-blame-ignore-revs.sh
1 #!/bin/sh
2
3 test_description='ignore revisions when blaming'
4 . ./test-lib.sh
5
6 # Creates:
7 #       A--B--X
8 # A added line 1 and B added line 2.  X makes changes to those lines.  Sanity
9 # check that X is blamed for both lines.
10 test_expect_success setup '
11         test_commit A file line1 &&
12
13         echo line2 >>file &&
14         git add file &&
15         test_tick &&
16         git commit -m B &&
17         git tag B &&
18
19         test_write_lines line-one line-two >file &&
20         git add file &&
21         test_tick &&
22         git commit -m X &&
23         git tag X &&
24         git tag -a -m "X (annotated)" XT &&
25
26         git blame --line-porcelain file >blame_raw &&
27
28         grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
29         git rev-parse X >expect &&
30         test_cmp expect actual &&
31
32         grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
33         git rev-parse X >expect &&
34         test_cmp expect actual
35 '
36
37 # Ensure bogus --ignore-rev requests are caught
38 test_expect_success 'validate --ignore-rev' '
39         test_must_fail git blame --ignore-rev X^{tree} file
40 '
41
42 # Ensure bogus --ignore-revs-file requests are silently accepted
43 test_expect_success 'validate --ignore-revs-file' '
44         git rev-parse X^{tree} >ignore_x &&
45         git blame --ignore-revs-file ignore_x file
46 '
47
48 for I in X XT
49 do
50         # Ignore X (or XT), make sure A is blamed for line 1 and B for line 2.
51         # Giving X (i.e. commit) and XT (i.e. annotated tag to commit) should
52         # produce the same result.
53         test_expect_success "ignore_rev_changing_lines ($I)" '
54                 git blame --line-porcelain --ignore-rev $I file >blame_raw &&
55
56                 grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
57                 git rev-parse A >expect &&
58                 test_cmp expect actual &&
59
60                 grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
61                 git rev-parse B >expect &&
62                 test_cmp expect actual
63         '
64 done
65
66 # For ignored revs that have added 'unblamable' lines, attribute those to the
67 # ignored commit.
68 #       A--B--X--Y
69 # Where Y changes lines 1 and 2, and adds lines 3 and 4.  The added lines ought
70 # to have nothing in common with "line-one" or "line-two", to keep any
71 # heuristics from matching them with any lines in the parent.
72 test_expect_success ignore_rev_adding_unblamable_lines '
73         test_write_lines line-one-change line-two-changed y3 y4 >file &&
74         git add file &&
75         test_tick &&
76         git commit -m Y &&
77         git tag Y &&
78
79         git rev-parse Y >expect &&
80         git blame --line-porcelain file --ignore-rev Y >blame_raw &&
81
82         grep -E "^[0-9a-f]+ [0-9]+ 3" blame_raw | sed -e "s/ .*//" >actual &&
83         test_cmp expect actual &&
84
85         grep -E "^[0-9a-f]+ [0-9]+ 4" blame_raw | sed -e "s/ .*//" >actual &&
86         test_cmp expect actual
87 '
88
89 # Ignore X and Y, both in separate files.  Lines 1 == A, 2 == B.
90 test_expect_success ignore_revs_from_files '
91         git rev-parse X >ignore_x &&
92         git rev-parse Y >ignore_y &&
93         git blame --line-porcelain file --ignore-revs-file ignore_x --ignore-revs-file ignore_y >blame_raw &&
94
95         grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
96         git rev-parse A >expect &&
97         test_cmp expect actual &&
98
99         grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
100         git rev-parse B >expect &&
101         test_cmp expect actual
102 '
103
104 # Ignore X from the config option, Y from a file.
105 test_expect_success ignore_revs_from_configs_and_files '
106         git config --add blame.ignoreRevsFile ignore_x &&
107         git blame --line-porcelain file --ignore-revs-file ignore_y >blame_raw &&
108
109         grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
110         git rev-parse A >expect &&
111         test_cmp expect actual &&
112
113         grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
114         git rev-parse B >expect &&
115         test_cmp expect actual
116 '
117
118 # Override blame.ignoreRevsFile (ignore_x) with an empty string.  X should be
119 # blamed now for lines 1 and 2, since we are no longer ignoring X.
120 test_expect_success override_ignore_revs_file '
121         git blame --line-porcelain file --ignore-revs-file "" --ignore-revs-file ignore_y >blame_raw &&
122         git rev-parse X >expect &&
123
124         grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
125         test_cmp expect actual &&
126
127         grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
128         test_cmp expect actual
129         '
130 test_expect_success bad_files_and_revs '
131         test_must_fail git blame file --ignore-rev NOREV 2>err &&
132         test_i18ngrep "cannot find revision NOREV to ignore" err &&
133
134         test_must_fail git blame file --ignore-revs-file NOFILE 2>err &&
135         test_i18ngrep "could not open.*: NOFILE" err &&
136
137         echo NOREV >ignore_norev &&
138         test_must_fail git blame file --ignore-revs-file ignore_norev 2>err &&
139         test_i18ngrep "invalid object name: NOREV" err
140 '
141
142 # For ignored revs that have added 'unblamable' lines, mark those lines with a
143 # '*'
144 #       A--B--X--Y
145 # Lines 3 and 4 are from Y and unblamable.  This was set up in
146 # ignore_rev_adding_unblamable_lines.
147 test_expect_success mark_unblamable_lines '
148         git config --add blame.markUnblamableLines true &&
149
150         git blame --ignore-rev Y file >blame_raw &&
151         echo "*" >expect &&
152
153         sed -n "3p" blame_raw | cut -c1 >actual &&
154         test_cmp expect actual &&
155
156         sed -n "4p" blame_raw | cut -c1 >actual &&
157         test_cmp expect actual
158 '
159
160 # Commit Z will touch the first two lines.  Y touched all four.
161 #       A--B--X--Y--Z
162 # The blame output when ignoring Z should be:
163 # ?Y ... 1)
164 # ?Y ... 2)
165 # Y  ... 3)
166 # Y  ... 4)
167 # We're checking only the first character
168 test_expect_success mark_ignored_lines '
169         git config --add blame.markIgnoredLines true &&
170
171         test_write_lines line-one-Z line-two-Z y3 y4 >file &&
172         git add file &&
173         test_tick &&
174         git commit -m Z &&
175         git tag Z &&
176
177         git blame --ignore-rev Z file >blame_raw &&
178         echo "?" >expect &&
179
180         sed -n "1p" blame_raw | cut -c1 >actual &&
181         test_cmp expect actual &&
182
183         sed -n "2p" blame_raw | cut -c1 >actual &&
184         test_cmp expect actual &&
185
186         sed -n "3p" blame_raw | cut -c1 >actual &&
187         ! test_cmp expect actual &&
188
189         sed -n "4p" blame_raw | cut -c1 >actual &&
190         ! test_cmp expect actual
191 '
192
193 # For ignored revs that added 'unblamable' lines and more recent commits changed
194 # the blamable lines, mark the unblamable lines with a
195 # '*'
196 #       A--B--X--Y--Z
197 # Lines 3 and 4 are from Y and unblamable, as set up in
198 # ignore_rev_adding_unblamable_lines.  Z changed lines 1 and 2.
199 test_expect_success mark_unblamable_lines_intermediate '
200         git config --add blame.markUnblamableLines true &&
201
202         git blame --ignore-rev Y file >blame_raw 2>stderr &&
203         echo "*" >expect &&
204
205         sed -n "3p" blame_raw | cut -c1 >actual &&
206         test_cmp expect actual &&
207
208         sed -n "4p" blame_raw | cut -c1 >actual &&
209         test_cmp expect actual
210 '
211
212 # The heuristic called by guess_line_blames() tries to find the size of a
213 # blame_entry 'e' in the parent's address space.  Those calculations need to
214 # check for negative or zero values for when a blame entry is completely outside
215 # the window of the parent's version of a file.
216 #
217 # This happens when one commit adds several lines (commit B below).  A later
218 # commit (C) changes one line in the middle of B's change.  Commit C gets blamed
219 # for its change, and that breaks up B's change into multiple blame entries.
220 # When processing B, one of the blame_entries is outside A's window (which was
221 # zero - it had no lines added on its side of the diff).
222 #
223 # A--B--C, ignore B to test the ignore heuristic's boundary checks.
224 test_expect_success ignored_chunk_negative_parent_size '
225         rm -rf .git/ &&
226         git init &&
227
228         test_write_lines L1 L2 L7 L8 L9 >file &&
229         git add file &&
230         test_tick &&
231         git commit -m A &&
232         git tag A &&
233
234         test_write_lines L1 L2 L3 L4 L5 L6 L7 L8 L9 >file &&
235         git add file &&
236         test_tick &&
237         git commit -m B &&
238         git tag B &&
239
240         test_write_lines L1 L2 L3 L4 xxx L6 L7 L8 L9 >file &&
241         git add file &&
242         test_tick &&
243         git commit -m C &&
244         git tag C &&
245
246         git blame file --ignore-rev B >blame_raw
247 '
248
249 # Resetting the repo and creating:
250 #
251 # A--B--M
252 #  \   /
253 #   C-+
254 #
255 # 'A' creates a file.  B changes line 1, and C changes line 9.  M merges.
256 test_expect_success ignore_merge '
257         rm -rf .git/ &&
258         git init &&
259
260         test_write_lines L1 L2 L3 L4 L5 L6 L7 L8 L9 >file &&
261         git add file &&
262         test_tick &&
263         git commit -m A &&
264         git tag A &&
265
266         test_write_lines BB L2 L3 L4 L5 L6 L7 L8 L9 >file &&
267         git add file &&
268         test_tick &&
269         git commit -m B &&
270         git tag B &&
271
272         git reset --hard A &&
273         test_write_lines L1 L2 L3 L4 L5 L6 L7 L8 CC >file &&
274         git add file &&
275         test_tick &&
276         git commit -m C &&
277         git tag C &&
278
279         test_merge M B &&
280         git blame --line-porcelain file --ignore-rev M >blame_raw &&
281
282         grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
283         git rev-parse B >expect &&
284         test_cmp expect actual &&
285
286         grep -E "^[0-9a-f]+ [0-9]+ 9" blame_raw | sed -e "s/ .*//" >actual &&
287         git rev-parse C >expect &&
288         test_cmp expect actual
289 '
290
291 test_done