13

git blame --ignore-revs-file is clearly an option that exists in modern Git.

There's only one problem. It doesn't work.

Or at least, it doesn't work for me:

You can add this in a shell script:

mkdir -p /tmp/blarp
cd /tmp/blarp
git init
cat << EOF > file.txt
one
two
three
EOF
git add file.txt
git commit --author "One <one@example.com>" -m 'one commit'
cat << EOF > file.txt
one
awesome
three
EOF
git add file.txt
git commit --author "Two <two@example.com>" -m 'two commits'
cat << EOF > file.txt
one
awesome
sauce
EOF
git add file.txt
git commit --author "One <one@example.com>" -m 'three commits'
git rev-parse HEAD~1 > ignore.txt
git blame --ignore-revs-file=ignore.txt file.txt

For me this shows:

^b6d40d5 (One 2019-12-30 21:47:15 +0000 1) one
1c185c4c (Two 2019-12-30 21:47:15 +0000 2) awesome
d8b9bafd (One 2019-12-30 21:47:15 +0000 3) sauce

But I expect to see

^b6d40d5 (One 2019-12-30 21:47:15 +0000 1) one
^b6d40d5 (One 2019-12-30 21:47:15 +0000 2) two
d8b9bafd (One 2019-12-30 21:47:15 +0000 3) sauce

or

^b6d40d5 (One 2019-12-30 21:47:15 +0000 1) one
d8b9bafd (One 2019-12-30 21:47:15 +0000 2) awesome
d8b9bafd (One 2019-12-30 21:47:15 +0000 3) sauce

But this isn't the case. I did discover that if the changes were only whitespace changes they would be ignored... but the git documentation isn't explicit about this, just suggesting that:

--ignore-revs-file

Ignore revisions listed in file, which must be in the same format as an fsck.skipList. This option may be repeated, and these files will be processed after any files specified with the blame.ignoreRevsFile config option. An empty file name, "", will clear the list of revs from previously processed files.

Any clue why git blame --ignore-revs-file=revs-to-ignore doesn't seem to work correctly for me?

Community
  • 1
  • 1
Wayne Werner
  • 49,299
  • 29
  • 200
  • 290
  • 1
    This is a good question - I've been able to repeat the effect and have tried turning on the `blame.markUnblamables` and `blame.markIgnoredLines` settings as well and they don't seem to do anything here either. This seems wrong, but the new blaming/assignment algorithms are full of heuristics and not very well described, so maybe it's intentional, or maybe it is a bug. – torek Dec 31 '19 at 18:02
  • Which version of git are you using? – Daniel Feb 07 '20 at 10:49
  • 2.24.0 - and others – Wayne Werner Feb 07 '20 at 16:44
  • Still seeing this in git 2.28.0 macOS – Devin Rhode Sep 15 '20 at 19:48
  • Anyone actually able to make this work? – Jesse Oct 02 '20 at 02:16
  • 1
    "maybe it's intentional, or maybe it is a bug" - It's intentional. ignore-rev is designed to ignore uninteresting commits. Completely changing the content of a line is interesting :) I agree this intent could be communicated more clearly in the docs. – Michael Platings Mar 28 '21 at 21:13

2 Answers2

4

Check if Git 2.29 (Q4 2020) has fixed the isue: "git blame --ignore-rev/--ignore-revs-file"(man) failed to validate their input are valid revision, and failed to take into account that the user may want to give an annotated tag instead of a commit, which has been corrected.

See commit 610e2b9, commit f58931c (24 Sep 2020) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster -- in commit 230ff3e, 04 Oct 2020)

blame: validate and peel the object names on the ignore list

The command reads list of object names to place on the ignore list either from the command line or from a file, but they are not checked with their object type (those read from the file are not even checked for object existence).

Extend the oidset_parse_file() API and allow it to take a callback that can be used to die (e.g. when an inappropriate input is read) or modify the object name read (e.g. when a tag pointing at a commit is read, and the caller wants a commit object name), and use it in the code that handles ignore list.


With Git 2.30 (Q1 2021), "git blame --ignore-revs-file=<file>(man)" learned to ignore a non-existent object name in the input, instead of complaining.

See commit c714d05 (10 Nov 2020) by René Scharfe (rscharfe).
(Merged by Junio C Hamano -- gitster -- in commit b4e245a, 18 Nov 2020)

blame: silently ignore invalid ignore file objects

Reported-by: Jean-Yves Avenard
Signed-off-by: René Scharfe
Reviewed-by: Barret Rhoden

Since 610e2b9240 ("blame: validate and peel the object names on the ignore list", 2020-09-24, Git v2.29.0-rc0 -- merge listed in batch #19) git blame(man) reports checks if objects specified with --ignore-rev and in files loaded with --ignore-revs-file and config option blame.ignoreRevsFile are actual objects and dies if they aren't. The intent is to report typos to the user.

This also breaks the ability to use a single ignore file for multiple repositories.
Typos are presumably less likely in files than on the command line, so alerting is less useful here.
Restore that feature by skipping non-commits without dying.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
2

TL;DR: the ignore-revs feature is designed for refactoring commits, not commits where lines are changed entirely.

Hi Wayne,

Thanks for trying it out. Sorry your first experience with it wasn't good. I think the docs could be improved to communicate the intent of the feature better.

git blame --ignore-rev is trying to find a line that is somewhat similar to "awesome". But "awesome" and "two" have nothing in common so it gives up and attributes "awesome" to the actual commit that added it.

There is a feature to identify this scenario, but it needs to be explicitly enabled:

If the blame.markUnblamableLines config option is set, then those lines touched by an ignored commit that we could not attribute to another revision are marked with a *.

Using this option, with your example script I see a * to indicate the problem:

^6bce3bb (One 2021-03-28 15:08:08 +0100 1) one
*b75aaf2 (Two 2021-03-28 15:08:08 +0100 2) awesome
5d3b18c7 (One 2021-03-28 15:08:08 +0100 3) sauce

If you try git blame --ignore-rev on a commit that you genuinely want to ignore (for example reformatting your code) then it should work well.

For example, if I change your script to replace two with awesome TWO then I see this:

^5307c74 (One 2021-03-28 15:14:52 +0100 1) one
^5307c74 (One 2021-03-28 15:14:52 +0100 2) awesome TWO
9d3fcc01 (One 2021-03-28 15:14:52 +0100 3) sauce
Michael Platings
  • 3,045
  • 2
  • 25
  • 17
  • What would cause a line to become unattributable to a prior commit? Like in my repo I see that some lines that were touched by an ignored commit are correctly attributed to a prior commit, and others are not (if I set `markUnblamableLines` then I see the ignored SHA and a `*` to indicate this). – Adam Parkin Mar 29 '21 at 20:14
  • 1
    @AdamParkin if the algorithm can't identify a similar line nearby then it will be "unblamable". But the algorithm is not simple so there might be more to it than that. If there's an example that you think could be handled better then I'd be happy to take a look. – Michael Platings Mar 29 '21 at 22:09
  • Don't have a specific one I can share, but I saw an example of one on a python project at work where Black reformatted a line that was a long expression (ie: `some_variable = some_func(with, a, lot, of, parameters, that, got, really, really, really, long)` such that it ended up with a single closing brace on a line, and that caused the blame ignore to fail to find a prior commit than the reformat commit. – Adam Parkin Mar 30 '21 at 22:00
  • I tried that and couldn't reproduce the issue. If you find a reproducer then please let me know and I'll see if I can improve things. Cheers – Michael Platings Apr 02 '21 at 14:55