rcmdnk's blog

20250727_gitprecommit_200_200

Gitのレポジトリに.pre-commit-config.yamlファイルが用意されていて pre-commitでリンターなどがかけられるようになっていても、 pre-commit installを忘れてしまうとpre-commitが動作しません。

手動でやろうとするとどうしても忘れてしまうので、 自動化する方法についていくつかやってみたものについて。

pre-commit

Gitのhookの機能で.git/hooks/pre-commitにスクリプトを置いておくと コミット時に自動的に実行され、問題があるとコミットが中断されるようになります。

その実行内容を簡単に管理できるようにするのが その名もpre-commitというツール。

pre-commit installを実行すると pre-commitツールが管理するツールを実行するための .git/hooks/pre-commitがインストールされます。

pre-commitコマンドは レポジトリルートに置かれた .pre-commit-config.yamlという設定ファイルに従って ツールを実行します。

.pre-commit-config.yamlや中で使うツールの設定などを適時レポジトリ内に追加しておけば コミット時に必要なチェックが行われ必要な形を担保できます。

ただ、pre-commit installを忘れてしまうとコミット時のチェックが行われません。

結構忘れたまま過ごしてしまうこともあるので、Gitのレポジトリを取ってきたら中で必ず pre-commit installが実行された状態になるような方法をいくつか試してみました。

基本的にはPythonで uv を使って管理されているレポジトリを想定していますが、 Pythonじゃなくてもそのまま使える部分もありますし 必要に応じて調整すれば使えるはずです。

Git alias

~/.gitconfigに以下のようなaliasを設定します。

.gitconfig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[alias]
  cl = "!f () {\
    git clone \"$@\";\
    ret=$?;\
    if [ \"$ret\" != 0 ];then \
      return $ret;\
    fi;\
    for last_arg; do :; done; \
    if [ -d \"$last_arg\" ];then \
      d=\"$last_arg\";\
    else \
      d=\"$(basename \"$last_arg\" .git)\";\
    fi;\
    cd \"$d\"; \
    if [ $? -ne 0 ];then \
      echo \"Failed to change directory to $d\";\
      return 1;\
    fi;\
    if type uv > /dev/null 2>&1 && [ -f uv.lock ];then \
      uv sync;\
    fi;\
    if [ -f .pre-commit-config.yaml ];then \
      if uv run which pre-commit > /dev/null 2>&1;then \
        uv run pre-commit install;\
      elif type pre-commit > /dev/null 2>&1;then \
        pre-commit install;\
      fi;\
    fi; \
  };f"

git cloneの代わりにgit clを使うと、 レポジトリをクローンした後に 特に最後の部分でレポジトリの中に.pre-commit-config.yamlがあればpre-commit installを実行するようにしています。

pre-commitコマンドはuvで管理される仮想環境化にあるのであればそれを使い、 そこにない場合にはグローバルにインストールされていればそれを使います。

そのため、その上で.uv.lockがあればuv syncを実行して 仮想環境の準備もしておきます。

この方法だと.gitconfigさえdotfilesなどで管理しておけば どの環境でもどうように使えますし、 またuvなどに関しても必要なものがあれば初期設定を追加することも出来ます。

これが一番簡単で確実な方法かな、と思います。 pre-commitに関してはこれでほぼほぼ問題ないはず。

ただ、あくまでaliasなので、普通にgit cloneしたら出来ないですし、 ghqみたいなレポジトリ管理ツールなどを使う場合にも 適用されないので それらのときでも同様のことをするためには必要に応じてwrapperを作るなどする必要があります。

Git init.templateDir

Gitではinit.templateDir.gitconfigに設定しておくと、 その中にあるhooksなどのディレクトリにあるファイルを .git/hooksなどにコピーしてくれます。

.gitの中にはhoooksinfoといったディレクトリが作られますが、 その中にあるファイルは元々Gitをインストールしたときに一緒にインストールされる git-core/templatesの中にあるものがコピーされます。

通常は$(git --exec-path)/../share/git-core/templates)のような場所にあります。 ここのhooksにはpre-commit.sampleなどのhookのサンプルが置かれていて、 クローンしたレポジトリの.git/hooks/pre-commitはそのサンプルが入っている状態になります。

init.templateDirにファイルを置いておくとこれらの代わりにinit.templateDirで指定したディレクトリの中にあるファイルが .gitにコピーされるようになります。

この設定のため、まず、.gitconfig

.gitconfig
1
2
[init]
  templateDir = ~/.config/git-templates

のような設定を追加するか、もしくはコマンドで、

1
$ git config --global init.templateDir ~/.config/.git-templates

を実行して設定を追加します。

テンプレートディレクトリの場所はどこでも良いのでHOME直下に.git-templatesとして作ったり好きなように。

pre-commitコマンドにはinit-templatedirというサブコマンドがあり、これによって指定したテンプレートディレクトリにpre-commitファイルを追加することができます。

もしpre-commitコマンドがグローバルにインストールされているのであれば、

1
$ pre-commit init-templatedir ~/.config/git-templates

で、~/.config/git-templates/hooks/pre-commitにpre-commitファイルが追加され、 次にCloneする際にこのファイルが追加されます。

一方、uvなどで管理している仮想環境化にpre-commitコマンドがインストールされている場合には、 これだと別のものになってしまいます。 逆にその仮想環境化で作ったpre-commitファイルはそのレポジトリ専用のものになってしまいます。

pre-commit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env bash
# start templated
INSTALL_PYTHON=</path/to>/python3
ARGS=(hook-impl --config=.pre-commit-config.yaml --hook-type=pre-commit)
# end templated

HERE="$(cd "$(dirname "$0")" && pwd)"
ARGS+=(--hook-dir "$HERE" -- "$@")

if [ -x "$INSTALL_PYTHON" ]; then
    exec "$INSTALL_PYTHON" -mpre_commit "${ARGS[@]}"
elif command -v pre-commit > /dev/null; then
    exec pre-commit "${ARGS[@]}"
else
    echo '`pre-commit` not found.  Did you forget to activate your virtualenv?' 1>&2
    exit 1
fi

最初のINSTALL_PYTHON<repo>/.venv/bin/python3のような Pythonのパスになってたりします。

これをいろいろな環境で完璧に合わせるのは難しいので最小限の状態にして かつ、pre-commitが設定されてないレポジトリでは実行しないように、

pre-commit
1
2
3
4
5
6
7
8
#!/usr/bin/env bash
if [ -f "$(git root)/.pre-commit-config.yaml" ]; then
  ARGS=(hook-impl --config=.pre-commit-config.yaml --hook-type=pre-commit)

  HERE="$(cd "$(dirname "$0")" && pwd)"
  ARGS+=(--hook-dir "$HERE" -- "$@")
  exec pre-commit "${ARGS[@]}"
fi

のようなスクリプトにしておくと 基本的にグローバルにインストールされているpre-commitコマンドが実行され、 必要であればレポジトリで仮想環境下に入って実行、もしくはuvならuv run git commitなどすれば その仮想環境下のpre-commitコマンドが実行されるようになります。

すべてのレポジトリにインストールされるので、.pre-commit-config.yamlがないレポジトリでは 実行しないようにしておく必要があります。

mise

miseはPythonだけでなく様々な言語の仮想環境を管理できるツールです。

このmiseを使っている場合に使える方法。

miseを有効にするために、レポジトリ.mise.tomlを作成しておく必要がありますが、 それを以下のような設定にしておきます。

.mise.toml
1
2
3
4
5
6
7
8
9
10
[env]
_.python.venv = ".venv"

[settings]
experimental = true

[hooks]
enter = [
  "[ -x \"$(git rev-parse --git-path hooks/pre-commit)\" ] || uv run pre-commit install >/dev/null"
]

miseにもhooksという機能がありますが、現状正式版にはなっていないため、 使うためにはexperimental = trueを設定しておく必要があります。

hooksはmiseの古いバージョンだと使えないかもしれないので 使えない場合はmiseのバージョンを更新してみてください。 (少なくともversion 2025.7.4だと使える)

hooksはディレクトリを移動するたびに実行されるcdや特定のファイルが変更されたときに実行されるwatch_filesなどがありますが、 enter/leaveといったレポジトリ内に入った際や出た際に実行されるものもあります。

このenterを使ってpre-comimt installを実行するようにしておきます。

ここではpre-commitコマンドがuvで仮想環境にインストールされるようになっているレポジトリを仮定しています。 もし、pre-commitコマンドをレポジトリ依存ではなくグローバルなものとして使ってる場合には単にpre-commit installに変更してください。

この方法の利点としては レポジトリ側での設定なのでレポジトリごとに必要に応じた設定ができること。

また、enterの指定なので、仮に間違って.git/hooks/pre-commitファイルを消してしまっても 次にそのレポジトリに入ったときに追加されます。

上の2つのGitだけの設定に比べると一般性は下がりますが、 その分レポジトリごとに必要な設定ができるので便利です。

欠点としてはmiseを使ってないと意味がないということと、 enterはレポジトリ下に入るたびに実行されるのでちょっと冗長かもしれないということ。

uv run pre-cmmit installは場合によっては実行時間が気になることもあるかもしれません。 なので上のようにpre-commitがインストールされてないときだけ実行するようにしてあります。

また、毎回uv runしてしまうとuv sync相当のことが毎回行われるのも場合によっては問題になるかもしれないので 基本的にはこのように必要なときだけ実行するようにしておいが方が良いと思います。

まとめ

cloneするときに、という点では最初のようなaliasを使えば pre-commitだけでなく、必要なものを自由に設定できるので 便利です。

ただ、cl意外を使ってしまう場合があれば適用されないので その場合を把握しておく必要があります。

init.templateDirを使う方法は pre-commitに関しては割と確実な方法ではありますが、 pre-commit関係ないレポジトリでも入れてしまったり、 どの環境のpre-commitを使うかで場合によって動かないこともあるので注意が必要です。

miseを使う方法だとレポジトリごとに必要な設定もできるので便利です ただmiseを使っていないと意味がないという欠点があります。 またレポジトリ側の設定なので自分で管理できるレポジトリでの話になります。

個人的な現状の設定としてはaliasとmiseの方法を入れてあります

Sponsored Links
Sponsored Links

« cVimがChromeのManifest V3で使えなくなったのでVimium Cに乗り換えた git worktreeの管理 »

}