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