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