25

I have about 40 git repositories, most of which have a .git-blame-ignore-revs file for using with git blame. So, instead of locally configuring each of them with blame.ignorerevsfile=.git-blame-ignore-revs, I applied it to my global git config.

However, the few repositories which do not have such a file are impacted by it: running git blame on them results in fatal: could not open object name list: .git-blame-ignore-revs.

Is there a way to tell git to read such file if it exists, ignoring it otherwise? That would be ideal: simple to setup, yet unobtrusive if no such file exists.

Note: another reason why having an optional .git-blame-ignore-revs file is useful, is that virtually all git repositories are created without such a file, and only later it is added. However, when bisecting old commits, we may end up returning to a point before the file exists, and then we have to touch it, finish bisecting, then remove it again to be able to return to the HEAD commit. Even if I use a local config for the file, this extremely annoying situation will still happen. For that reason, I do believe the default behavior should be to ignore the file if it does not exist.

anol
  • 8,264
  • 3
  • 34
  • 78

2 Answers2

10

Is there a way to tell git to read such file if it exists, ignoring it otherwise?

There is no. Either use

git config --local blame.ignoreRevsFile .git-blame-ignore-revs

instead of --global. Or use

git config --local blame.ignoreRevsFile ""

in those repositories where the file doesn't exist. Empty file name means "do not use any file".

Or create empty .git-blame-ignore-revs files.

Upd. Test with a new temporary repository:

git init test-git
cd test-git

git config --global blame.ignoreRevsFile test
git config --show-origin blame.ignoreRevsFile
file:/home/phd/.gitconfig       test

git config --local blame.ignoreRevsFile ""
git config --show-origin blame.ignoreRevsFile
file:.git/config

Upd2

git blame testfile
fatal: could not open object name list: test

Ouch!

phd
  • 82,685
  • 13
  • 120
  • 165
  • It would be great if the local configuration took over from the global one, allowing the former to be ignored. However, despite your link saying that *Empty file names will reset the list of ignored revisions*, and [this page](http://git-scm.com/docs/git-config#FILES) saying that "The files are read in the order given above [global before local], with last value found taking precedence over values read earlier", it still does not work, likely due to this behavior: "When multiple values are taken then all values of a key from all files will be used." – anol Dec 21 '21 at 14:42
  • @anol Please show the result of `git config --show-origin blame.ignoreRevsFile` in a repo without the file. – phd Dec 21 '21 at 14:50
  • It shows `file:$HOME/.gitconfig .git-blame-ignore-revs`, as expected, since I set it via `--global` previously. It does seem to me there is a slight behavior deviation from the documentation. Even when adding option `--ignore-revs-file ""`, which is [documented](https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt) as *An empty file name, "", will clear the list of revs from previously processed files*, it doesn't seem to be the case for me: git still searches for the `.git-blame-ignore-revs` file and the command fails. – anol Dec 21 '21 at 15:00
  • @anol Works for me. See the *Upd*ate test in the answer. – phd Dec 21 '21 at 15:06
  • Ok, I just ran the commands as in your snippet, but still, when I run `git blame ` inside the newly-created `test-git` repository, I get: `fatal: could not open object name list: test`. Could you please confirm that it works for you, and tell me your git version? I'm on git 2.31.1. – anol Dec 21 '21 at 15:13
  • 1
    @anol Oops, you're right! See *Upd2*. I'd say this is a bug in `git blame`. – phd Dec 21 '21 at 15:44
  • 4
    Apparently a [related patch](https://lore.kernel.org/git/20210808174847.16590-1-noah.pendleton@gmail.com/) has been proposed to git in August, but since then I think it hasn't advanced much. But something similar might land in a future Git version. – anol Dec 21 '21 at 16:16
  • 1
    If anyone sees a GitHub issue / any further activity on this please share the link so we can follow it. Thank you! – tony Jul 24 '22 at 14:20
1

Well, this isn't a perfect solution since it won't apply to a raw git blame, but I used a shell command alias. Obviously change the command as necessary, but FWIW I did get much better results for my use-cases with '-w' (ignore whitespace) supplied:

git config --global alias.bl "!myGitBlame.sh"

And the corresponding shell script (which is assumed to be on the PATH for the preceding git config command):

#!/usr/bin/env bash
if [[ -z $GIT_PREFIX ]]; then
    GIT_PREFIX=./
fi

# Assume (and impose limitation) that the last argument to this script is the
# file being blamed. Pull it out of the args list and make it relative to the
# root of the git repo to match the execution context of git shell aliases
LAST_ARG="${@: -1}"
set -- "${@:1:$(($#-1))}"
FILE_PATH=$GIT_PREFIX$LAST_ARG

# optional ignore revs file, specified relative to the root of the git repo
OPTIONAL_GIT_BLAME_REVS_FILE=.git-blame-ignore-revs

# if the revs file does not exist, ignore-revs-file accepts empty values
# without issue
if [[ ! -f $OPTIONAL_GIT_BLAME_REVS_FILE ]]; then
    OPTIONAL_GIT_BLAME_REVS_FILE=""
fi

git blame -w --ignore-revs-file=$OPTIONAL_GIT_BLAME_REVS_FILE "$@" $FILE_PATH

And as a one-liner if you want to pass it along to coworkers easily:

git config --global alias.bl '!if [[ -z $GIT_PREFIX ]]; then GIT_PREFIX=./; fi; f() { LAST_ARG="${@: -1}"; set -- "${@:1:$(($#-1))}"; FILE_PATH=$GIT_PREFIX$LAST_ARG; GIT_BLAME_REVS_FILE=.git-blame-ignore-revs; if [[ ! -f $GIT_BLAME_REVS_FILE ]]; then GIT_BLAME_REVS_FILE=""; fi; git blame -w --ignore-revs-file=$GIT_BLAME_REVS_FILE "$@" $FILE_PATH; }; f'

NOTE: This makes the assumption that the last argument to git blame is the file; that means this isn't 1:1 with what a normal git blame command line accepts because you can technically git blame <commit_hash> <file> or git blame <file> <commit_hash> in simple cases.

I definitely use this in the shell script form, but I am aware of the advantages of the 1-liner config for propagating this to coworkers and avoiding the PATH in general.

I am also assuming a bash-available environment here.

I had initially tried to just do a local config that points directly at the file, but that runs into all sorts of issues you outlined, and it interacts very poorly with worktree setups as well. This should work more generally, though not completely perfectly.

Ryan Ulch
  • 46
  • 4