Git fork

blame: print unblamable and ignored commits in porcelain mode

The 'git-blame(1)' command allows users to ignore specific revisions via
the '--ignore-rev <rev>' and '--ignore-revs-file <file>' flags. These
flags are often combined with the 'blame.markIgnoredLines' and
'blame.markUnblamableLines' config options. These config options prefix
ignored and unblamable lines with a '?' and '*', respectively.

However, this option was never extended to the porcelain mode of
'git-blame(1)'. Since the documentation does not indicate this
exclusion, it is a bug.

Fix this by printing 'ignored' and 'unblamable' respectively for the
options when using the porcelain modes.

Helped-by: Patrick Steinhardt <ps@pks.im>
Helped-by: Toon Claes <toon@iotcl.com>
Helped-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Karthik Nayak and committed by
Junio C Hamano
4d253071 683c54c9

+60 -5
+2 -1
Documentation/blame-options.adoc
··· 125 125 another commit will be marked with a `?` in the blame output. If the 126 126 `blame.markUnblamableLines` config option is set, then those lines touched 127 127 by an ignored commit that we could not attribute to another revision are 128 - marked with a '*'. 128 + marked with a '*'. In the porcelain modes, we print 'ignored' and 129 + 'unblamable' on a newline respectively. 129 130 130 131 --ignore-revs-file <file>:: 131 132 Ignore revisions listed in `file`, which must be in the same format as an
+5 -4
Documentation/git-blame.adoc
··· 135 135 The porcelain format generally suppresses commit information that has 136 136 already been seen. For example, two lines that are blamed to the same 137 137 commit will both be shown, but the details for that commit will be shown 138 - only once. This is more efficient, but may require more state be kept by 139 - the reader. The `--line-porcelain` option can be used to output full 140 - commit information for each line, allowing simpler (but less efficient) 141 - usage like: 138 + only once. Information which is specific to individual lines will not be 139 + grouped together, like revs to be marked 'ignored' or 'unblamable'. This 140 + is more efficient, but may require more state be kept by the reader. The 141 + `--line-porcelain` option can be used to output full commit information 142 + for each line, allowing simpler (but less efficient) usage like: 142 143 143 144 # count the number of lines attributed to each author 144 145 git blame --line-porcelain file |
+15
builtin/blame.c
··· 351 351 write_filename_info(suspect); 352 352 } 353 353 354 + /* 355 + * Information which needs to be printed per-line goes here. Any 356 + * information which can be clubbed on a commit/file level, should 357 + * be printed via 'emit_one_suspect_detail()'. 358 + */ 359 + static void emit_porcelain_per_line_details(struct blame_entry *ent) 360 + { 361 + if (mark_unblamable_lines && ent->unblamable) 362 + puts("unblamable"); 363 + if (mark_ignored_lines && ent->ignored) 364 + puts("ignored"); 365 + } 366 + 354 367 static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent, 355 368 int opt) 356 369 { ··· 367 380 ent->lno + 1, 368 381 ent->num_lines); 369 382 emit_porcelain_details(suspect, repeat); 383 + emit_porcelain_per_line_details(ent); 370 384 371 385 cp = blame_nth_line(sb, ent->lno); 372 386 for (cnt = 0; cnt < ent->num_lines; cnt++) { ··· 377 391 ent->lno + 1 + cnt); 378 392 if (repeat) 379 393 emit_porcelain_details(suspect, 1); 394 + emit_porcelain_per_line_details(ent); 380 395 } 381 396 putchar('\t'); 382 397 do {
+38
t/t8013-blame-ignore-revs.sh
··· 158 158 test_cmp expect actual 159 159 ' 160 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 + 161 180 # Commit Z will touch the first two lines. Y touched all four. 162 181 # A--B--X--Y--Z 163 182 # The blame output when ignoring Z should be: ··· 190 209 sed -n "4p" blame_raw | cut -c1 >actual && 191 210 ! test_cmp expect actual 192 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 193 231 194 232 # For ignored revs that added 'unblamable' lines and more recent commits changed 195 233 # the blamable lines, mark the unblamable lines with a