git blame with optional ignorerevsfile

7.7k Views Asked by At

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.

2

There are 2 best solutions below

8
phd On

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!

0
Ryan Ulch On

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.