## 概要 [[Lazygit]]でコミットメッセージを書くときに[[GitHub Copilot]]の補完機能で楽ができるかを試してみたくなったところ、以下の記事を見つけた。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://static.zenn.studio/images/logo-transparent.png" /> <span class="link-card-v2-site-name">Zenn</span> </div> <div class="link-card-v2-title"> Neovim × lazygit でコミットメッセージを楽に書く </div> <img class="link-card-v2-image" src="https://res.cloudinary.com/zenn/image/upload/s--9jgmZQYw--/c_fit%2Cg_north_west%2Cl_text:notosansjp-medium.otf_55:Neovim%2520%25C3%2597%2520%2520lazygit%2520%25E3%2581%25A7%25E3%2582%25B3%25E3%2583%259F%25E3%2583%2583%25E3%2583%2588%25E3%2583%25A1%25E3%2583%2583%25E3%2582%25BB%25E3%2583%25BC%25E3%2582%25B8%25E3%2582%2592%25E6%25A5%25BD%25E3%2581%25AB%25E6%259B%25B8%25E3%2581%258F%2Cw_1010%2Cx_90%2Cy_100/g_south_west%2Cl_text:notosansjp-medium.otf_37:nabekou29%2Cx_203%2Cy_121/g_south_west%2Ch_90%2Cl_fetch:aHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EtL0FPaDE0R2pMeDlwcWpWUGtnS2lVckVmYlVncWUyc2F6VmEwbVdVQ3B1X0U9czI1MC1j%2Cr_max%2Cw_90%2Cx_87%2Cy_95/v1627283836/default/og-base-w1200-v2.png?_a=BACAGSGT" /> <a href="https://zenn.dev/nabekou29/articles/neovim-lazygit-commit-message"></a> </div> AIを使ったコミットメッセージ生成に興味はなかったが、[[コミットテンプレート (Git)|コミットテンプレート]]を[[GitHub Copilot]]のプロンプトとして利用するという発想がなかったので、ちょっと試してみたくなった。 > [!hint] > 動作イメージを知りたければ [[#動作の様子]] を最初に参照。 ## 戦略 ### 基本 [[Lazygit]]でコミットメッセージを編集する方法は大きく2種類ある。 1. [[TUI]]上で操作する 2. 外部エディタを立ち上げて操作する 1の方法ではできることが限られているため、2の方法を利用する。外部エディタには[[Neovim]]を使う。 ### 詳細 `Shift+c` で[[Neovim]]が起動する[[バッファ (Vim)|バッファ]]にはルールがある。 - ファイルパスは `.git/COMMIT_EDITMSG` - [[ファイルタイプ (Vim)|ファイルタイプ]]は `gitcommit` これを利用して、以下2つの設定を行う。 - [[Neovim]]の[[copilot.lua]]を有効にする - コミットメッセージバッファに最後に[[GitHub Copilot]]が必要なコンテキストをメッセージとして追記する ## [[Neovim]]の[[copilot.lua]]を有効にする [[copilot.lua]]の設定で `opts.filetypes.gitcommit` に `true` を設定する。 ```lua { opts = { filetypes = { gitcommit = true, }, }, } ``` これで `gitcommit` の[[ファイルタイプ (Vim)|ファイルタイプ]]バッファに対しても[[copilot.lua]]が有効になる。 ## コミットメッセージバッファにコンテキストを追加 `after/ftplugin/gitcommit.lua` を追加する。 ```lua -- COMMIT_EDITMSG を開いたときにステージ済み差分をコメントとして末尾へ追記する local function is_commit_editmsg() return vim.fn.expand("%:t") == "COMMIT_EDITMSG" end local function git_command(git_dir, repo_root, args) local command = { "git", "--git-dir=" .. git_dir, "--work-tree=" .. repo_root } vim.list_extend(command, args) return vim.fn.systemlist(command) end local function resolve_git_paths() local commit_path = vim.fn.expand("%:p") local git_dir = vim.fn.fnamemodify(commit_path, ":h") local repo_root = vim.fn.fnamemodify(git_dir, ":h") if git_dir == "" or repo_root == "" then return nil, nil end return git_dir, repo_root end local function fetch_recent_logs(git_dir, repo_root) local logs = git_command(git_dir, repo_root, { "log", "-10", "--pretty=%s", }) if vim.v.shell_error ~= 0 then return {} end return logs end local function should_prefer_japanese(log_lines) if #log_lines == 0 then return true end local re = vim.regex("\\v[ぁ-んァ-ン一-龠々〆ヵヶー]+") for _, line in ipairs(log_lines) do if re:match_str(line) then return true end end return false end local function fetch_staged_diff(git_dir, repo_root) local diff = git_command(git_dir, repo_root, { "diff", "--cached", }) if vim.v.shell_error ~= 0 or #diff == 0 then return nil end return diff end local function build_header_lines(log_lines, use_japanese) local header = {} local has_logs = #log_lines > 0 if use_japanese then table.insert(header, "# 以下の差分を参考にコミットメッセージを書いて。") table.insert(header, "# コミットメッセージは日本語で書いてください。英語は禁止!") table.insert( header, "# Conventional Commitを採用しています。直近のコミットメッセージを参考にしてください。" ) table.insert(header, "# ") if has_logs then table.insert(header, "# 直近のコミットメッセージ(最新10件):") end else table.insert(header, "# Write the commit message referring to the staged diff.") table.insert(header, "# Must write the commit message in English.") table.insert(header, "# We use Conventional Commit. Please refer to recent commit messages.") table.insert(header, "# ") if has_logs then table.insert(header, "# Recent commit messages (latest 10):") end end return header end local function build_log_lines(log_lines) local lines = {} for _, msg in ipairs(log_lines) do if msg ~= "" then table.insert(lines, "# - " .. msg) end end if #lines > 0 then table.insert(lines, "# ") end return lines end local function build_diff_lines(diff_lines) local lines = { "# --- Staged Diff (for Copilot) ---" } for _, line in ipairs(diff_lines) do table.insert(lines, "# " .. line) end return lines end local function build_injected_lines(log_lines, diff_lines, use_japanese) local lines = { "" } vim.list_extend(lines, build_header_lines(log_lines, use_japanese)) vim.list_extend(lines, build_log_lines(log_lines)) vim.list_extend(lines, build_diff_lines(diff_lines)) return lines end if not is_commit_editmsg() then return end if vim.b._commit_diff_injected then return end -- 同一バッファでの重複挿入を防ぐ vim.b._commit_diff_injected = true local git_dir, repo_root = resolve_git_paths() if git_dir == nil or repo_root == nil then return end local log_lines = fetch_recent_logs(git_dir, repo_root) local use_japanese = should_prefer_japanese(log_lines) local diff_lines = fetch_staged_diff(git_dir, repo_root) if diff_lines == nil then return end vim.api.nvim_buf_set_lines(0, -1, -1, false, build_injected_lines(log_lines, diff_lines, use_japanese)) ``` コードはすべて[[Codex CLI]]に委ねたので無駄な記述や謎の記述はあるかもしれない。『自己レビュー+修正』を3周させているので、そこそこはリファクタリングされている。はず。 ## 動作の様子 [[🦉Toki]]に加えてみた変更で試してみる。以下が差分。 ```diff diff --git a/mnt/toki/toki.sh b/mnt/toki/toki.sh index f5160d9..0a66824 100755 --- a/mnt/toki/toki.sh +++ b/mnt/toki/toki.sh @@ -46,6 +46,7 @@ Available targets | go-sqlx | Go | - | Go | sqlx + mysql + air | golangci-lint | - | | rust | Rust | - | Cargo | - | - | - | | python | Python | Virtualenv | Pip | - | ruff | ruff | +| uv | Python | uv | uv | - | ruff | ruff | | nvim | Lua | Lua | | nvim | - | - | | nvimapp | Lua | Neovim | lazy | - | - | - | | bash | Bash | Bash | | - | - | - | @@ -502,6 +503,30 @@ $ mise watch dev exit 0 fi +# ------------------------------------------- +# uv +# ------------------------------------------- +if [[ $command == "uv" ]]; then + path="${1:?'pathは必須です'}" + + mkdir -p "$path" + cd "$path" + + git init + uv init --bare + uv add ruff + + cp -r "${TEMPLATE_DIR}"/uv/* . + + echo " +🚀 Try + +$ cd ${path} +$ mise watch dev +" + exit 0 +fi + # ------------------------------------------- # nvim # ------------------------------------------- ``` [[Lazygit]]ですべて[[ステージング (Git)|ステージング]]してから、`Shift+c` を押してみる。 ![[2025-12-06-20-51-42.avif|frame]] *赤枠が今回追加された内容* [[Conventional Commits]]のtypeを入力すると、あとは補完される。 ![[2025-12-06-20-53-23.avif]] 自分の設定の問題かもしれないが、typeを入力しないと補完が出なかったり、精度が下がったりする。typeくらいは意識してもいいのかもしれない。 なお、最終的なコミットメッセージは以下のように修正したので、精度としては **『無いよりはマシ』** くらいのもの。 ``` feat(toki): toki uvコマンド追加 ``` 過去のコミットメッセージ数を増やしたり、ルールをしっかり伝えれば精度は上がるかもしれないが、そこまでやる価値は感じていない。 ## 思ったこと 正直、今のところは恩恵は感じない... がまずは1週間試してみる。 日本語のコミットメッセージはそこまで苦ではないため、コメント行に書いた日本語を英語に翻訳するような[[コミットテンプレート (Git)|コミットテンプレート]]の方がいいのかもしれない。