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>
This commit is contained in:
Karthik Nayak
2025-04-03 18:03:26 +02:00
committed by Junio C Hamano
parent 683c54c999
commit 4d253071dd
4 changed files with 60 additions and 5 deletions

View File

@@ -125,7 +125,8 @@ take effect.
another commit will be marked with a `?` in the blame output. If the another commit will be marked with a `?` in the blame output. If the
`blame.markUnblamableLines` config option is set, then those lines touched `blame.markUnblamableLines` config option is set, then those lines touched
by an ignored commit that we could not attribute to another revision are by an ignored commit that we could not attribute to another revision are
marked with a '*'. marked with a '*'. In the porcelain modes, we print 'ignored' and
'unblamable' on a newline respectively.
--ignore-revs-file <file>:: --ignore-revs-file <file>::
Ignore revisions listed in `file`, which must be in the same format as an Ignore revisions listed in `file`, which must be in the same format as an

View File

@@ -135,10 +135,11 @@ header elements later.
The porcelain format generally suppresses commit information that has The porcelain format generally suppresses commit information that has
already been seen. For example, two lines that are blamed to the same already been seen. For example, two lines that are blamed to the same
commit will both be shown, but the details for that commit will be shown commit will both be shown, but the details for that commit will be shown
only once. This is more efficient, but may require more state be kept by only once. Information which is specific to individual lines will not be
the reader. The `--line-porcelain` option can be used to output full grouped together, like revs to be marked 'ignored' or 'unblamable'. This
commit information for each line, allowing simpler (but less efficient) is more efficient, but may require more state be kept by the reader. The
usage like: `--line-porcelain` option can be used to output full commit information
for each line, allowing simpler (but less efficient) usage like:
# count the number of lines attributed to each author # count the number of lines attributed to each author
git blame --line-porcelain file | git blame --line-porcelain file |

View File

@@ -351,6 +351,19 @@ static void emit_porcelain_details(struct blame_origin *suspect, int repeat)
write_filename_info(suspect); write_filename_info(suspect);
} }
/*
* Information which needs to be printed per-line goes here. Any
* information which can be clubbed on a commit/file level, should
* be printed via 'emit_one_suspect_detail()'.
*/
static void emit_porcelain_per_line_details(struct blame_entry *ent)
{
if (mark_unblamable_lines && ent->unblamable)
puts("unblamable");
if (mark_ignored_lines && ent->ignored)
puts("ignored");
}
static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent, static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
int opt) int opt)
{ {
@@ -367,6 +380,7 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
ent->lno + 1, ent->lno + 1,
ent->num_lines); ent->num_lines);
emit_porcelain_details(suspect, repeat); emit_porcelain_details(suspect, repeat);
emit_porcelain_per_line_details(ent);
cp = blame_nth_line(sb, ent->lno); cp = blame_nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) { for (cnt = 0; cnt < ent->num_lines; cnt++) {
@@ -377,6 +391,7 @@ static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
ent->lno + 1 + cnt); ent->lno + 1 + cnt);
if (repeat) if (repeat)
emit_porcelain_details(suspect, 1); emit_porcelain_details(suspect, 1);
emit_porcelain_per_line_details(ent);
} }
putchar('\t'); putchar('\t');
do { do {

View File

@@ -158,6 +158,25 @@ test_expect_success mark_unblamable_lines '
test_cmp expect actual test_cmp expect actual
' '
for opt in --porcelain --line-porcelain
do
test_expect_success "mark_unblamable_lines with $opt" "
sha=$(git rev-parse Y) &&
git -c blame.markUnblamableLines=false blame $opt --ignore-rev Y file >raw &&
cat > sedscript <<- 'EOF' &&
/^ y3/i\\
unblamable
/^ y4/i\\
unblamable
EOF
sed -f sedscript raw >expect &&
git -c blame.markUnblamableLines=true blame $opt --ignore-rev Y file >actual &&
test_cmp expect actual
"
done
# Commit Z will touch the first two lines. Y touched all four. # Commit Z will touch the first two lines. Y touched all four.
# A--B--X--Y--Z # A--B--X--Y--Z
# The blame output when ignoring Z should be: # The blame output when ignoring Z should be:
@@ -191,6 +210,25 @@ test_expect_success mark_ignored_lines '
! test_cmp expect actual ! test_cmp expect actual
' '
for opt in --porcelain --line-porcelain
do
test_expect_success "mark_ignored_lines with $opt" "
sha=$(git rev-parse Y) &&
git -c blame.markIgnoredLines=false blame $opt --ignore-rev Z file >raw &&
cat > sedscript <<- 'EOF' &&
/^ line-one-Z/i\\
ignored
/^ line-two-Z/i\\
ignored
EOF
sed -f sedscript raw >expect &&
git -c blame.markIgnoredLines=true blame $opt --ignore-rev Z file >actual &&
test_cmp expect actual
"
done
# For ignored revs that added 'unblamable' lines and more recent commits changed # For ignored revs that added 'unblamable' lines and more recent commits changed
# the blamable lines, mark the unblamable lines with a # the blamable lines, mark the unblamable lines with a
# '*' # '*'