👻

--ignore-revオプションで特定コミットをgit blameから除外する

2023/02/09に公開

はじめに

突然ですがみなさんはgit blameしてますか?
私は毎日git blameしています!
git blameした結果、その差分をコミットしたのは過去の自分と知るのは日常茶飯事です。

git blameはコードリーディングの強力な味方ですが、LinterやFormatterをコードベース全体に適用すると、差分の情報がそれらで上書きされてしまい情報量が減ってしまいます。
そのような場合はgit historyで歴史を遡るしかないのでしょうか?
いいえ、git v2.23から追加された--ignore-revオプションで指定したコミットをblameから除外することができるようになりました。

git blame --ignore-revの使い方

--ignore-revにコミットハッシュを渡すとそのコミットはblameから除外されます。

# コミットハッシュは架空のものです
$ git blame --ignore-rev MyF4EkvOZNVKKJKqJv2pV8a2PBKWsyxfK4lTlMya

blame.ignoreRevsFileを設定して常にIgnoreされるようにする

毎回--ignore-revを指定していては開発者体験が良くありません。
そんなあなたにgit-configのオプションが存在します。

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

.git-blame-ignore-revsにはコミットハッシュを羅列するだけですがそれが何をIgnoreするのかコメントをつけるとわかりやすいでしょう。

# Prettierで全コードをフォーマット
MyF4EkvOZNVKKJKqJv2pV8a2PBKWsyxfK4lTlMya

ignoreRevsFileは任意のファイル名を指定することが可能ですが、GitHubは.git-blame-ignore-revsのファイル名で作成するとWeb UIのblameでIgnoreしてくれるため、特別な理由がない限りこのファイル名がおすすめです。
GitLabも同じファイル名で対応するかどうかが議論されています。

https://github.blog/changelog/2022-03-24-ignore-commits-in-the-blame-view-beta/
https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-view
https://gitlab.com/gitlab-org/gitlab/-/issues/31423

blame.markIgnoredLinesを設定してIgnoreされた箇所に?マークをつける

--ignore-revまたはblame.ignoreRevsFile指定時にblame.markIgnoredLines trueを設定するとIgnoreされた場合に?マークをつけてお知らせしてくれます。(後述)

git config --global blame.markIgnoredLines true

やってみる

実際に動かして動作を確認してみます。

下準備

$ mkdir hoge && cd hoge
$ cat << EOF > fuga
foo
bar
bazz
EOF
$ cat fuga
foo
bar
bazz
$ git init

コミット作成

# コミット1回目
$ git add fuga
$ git commit --author 'Zenn<zenn@example.com>' -m 'Initial commit'
[main (root-commit) 552350b] Initial commit
 Author: Zenn <zenn@example.com>
 1 file changed, 3 insertions(+)
 create mode 100644 fuga
$ git blame fuga
^552350b (Zenn 2023-02-09 19:34:44 +0900 1) foo
^552350b (Zenn 2023-02-09 19:34:44 +0900 2) bar
^552350b (Zenn 2023-02-09 19:34:44 +0900 3) bazz

# コミット2回目
# barの前にスペースを2個追加
$ gsed -i 's/bar/  bar/g' fuga
$ git add fuga
$ git commit --author 'Zenn2<zenn2@example.com>' -m 'Second commit'
[main 5306907] Second commit
 Author: Zenn2 <zenn2@example.com>
 1 file changed, 1 insertion(+), 1 deletion(-)
$ git blame fuga
^552350b (Zenn  2023-02-09 19:34:44 +0900 1) foo
53069075 (Zenn2 2023-02-09 19:54:10 +0900 2)   bar
^552350b (Zenn  2023-02-09 19:34:44 +0900 3) bazz

# コミット3回目
$ gsed -i 's/bazz/changed/g' fuga
$ git add fuga
$ git commit --author 'Zenn3<zenn3@example.com>' -m 'Third commit'
[main e85f232] Third commit
 Author: Zenn3 <zenn3@example.com>
 1 file changed, 1 insertion(+), 1 deletion(-)
$ git blame fuga
^552350b (Zenn  2023-02-09 19:34:44 +0900 1) foo
53069075 (Zenn2 2023-02-09 19:54:10 +0900 2)   bar
e85f2322 (Zenn3 2023-02-09 19:55:36 +0900 3) changed

git blame --ignore-rev

2回目のコミットをIgnoreします

$ git rev-parse HEAD^1
530690759d090e1af427abfc444a15fe817ca80a
$ git blame --ignore-rev 530690759d090e1af427abfc444a15fe817ca80a fuga
^552350b (Zenn  2023-02-09 19:34:44 +0900 1) foo
^552350b (Zenn  2023-02-09 19:34:44 +0900 2)   bar
e85f2322 (Zenn3 2023-02-09 19:55:36 +0900 3) changed

2行目の barはコード差分がありますが1回目のコミットのZennがコミットとして表示されています。

blame.ignoreRevsFile

$ cat << EOF > .git-blame-ignore-revs
530690759d090e1af427abfc444a15fe817ca80a
EOF
# --globalオプションをつけていないのでこのリポジトリのみ有効な設定
$ git config blame.ignoreRevsFile .git-blame-ignore-revs
$ git blame fuga
^552350b (Zenn  2023-02-09 19:34:44 +0900 1) foo
^552350b (Zenn  2023-02-09 19:34:44 +0900 2)   bar
e85f2322 (Zenn3 2023-02-09 19:55:36 +0900 3) changed

blame.markIgnoredLines true

git config blame.markIgnoredLines true
/Users/takanobuhoshino/Downloads/hoge% git blame fuga
^552350b (Zenn  2023-02-09 19:34:44 +0900 1) foo
^?552350 (Zenn  2023-02-09 19:34:44 +0900 2)   bar
e85f2322 (Zenn3 2023-02-09 19:55:36 +0900 3) changed

2行目のコミットハッシュに?が挿入されていることがわかります。
(^はboundary commitでこの場合は初回のコミットを指すマーク)

blame.markUnblamableLines trueについて

--ignore-revのオプションにはblame.markUnblamableLinesもありますが、どのような機能か説明から理解できなかったので公式ドキュメントの説明を添えるにとどめます。

Mark lines that were changed by an ignored revision that we could not attribute to another commit with a * in the output of git-blame[1].

GitLens利用時の注意点

便利な--ignore-revですが人気のVSCode拡張機能であるGitLens利用時に問題がありました。
blame.ignoreRevsFileを設定している場合に、指定したファイルが存在しない場合はGitLensのblameが正常に機能しませんでした。
Issueが上がっていますが解決はしていないためGitLensユーザーの方は設定に気をつけましょう。

https://github.com/gitkraken/vscode-gitlens/issues/1143

まとめ

--ignore-revオプションを使うことでgit blameから特定のコミットを除外することができました。
はじめににも書いたように、特にLinterやFormatterをリポジトリに全体に適用したケースに有効なオプションです。
blameに支障が出るから実行に躊躇しているような方はぜひ設定してみてください。

参考資料

https://www.git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt
https://www.git-scm.com/docs/git-blame#Documentation/git-blame.txt-blameignoreRevsFile
https://www.michaelheap.com/git-ignore-rev/
https://stackoverflow.com/questions/59537270/why-doesnt-git-blame-ignore-rev-ignore-revs-file-work-for-me

GitHubで編集を提案

Discussion