Claude Media
Claude Codeでgit-cliffのCHANGELOGを整える — 誤分類を直して利用者向けに

Claude Codeでgit-cliffのCHANGELOGを整える — 誤分類を直して利用者向けに

git-cliffがコミット履歴から作ったCHANGELOGを、Claude Codeでcliff.tomlの割り当て修正と文面の書き換えに分けて直し、Keep a Changelog形式に整える手順をまとめます。

git-cliffは、Gitの履歴からCHANGELOGを生成するツールです。コミットメッセージを正規表現で仕分けし、テンプレートで好みの形に出力します。Claude Codeに任せやすいのは、この仕分けルール(cliff.toml)を書く作業と、生成された変更履歴の誤分類を直して利用者向けの文に整える作業です。

ただし、直す場所を間違えると効果が出ません。同じ型の誤分類が毎回出るならcliff.tomlを直します。今回限りの言い回しの問題なら、出力された文面を直します。この記事では、この2つを分けて進める流れを、設定例とコマンドつきで扱います。

自動リリースまで含めて任せたい場合は、Claude Codeでrelease-pleaseのリリースPRを整える手順やClaude Codeでsemantic-releaseを回す手順が別の入口になります。git-cliffはリリースを実行せず、変更履歴の生成だけを担当します。

Keep a Changelogは「git logを貼るな」と言っている

git-cliffの生成物をKeep a Changelog形式に寄せる、という話には最初に確認しておくことがあります。Keep a Changelogのサイトは、コミットログの差分をそのまま変更履歴にするのは悪い考えだと書いています。理由はノイズです。マージコミット、題名が分かりにくいコミット、ドキュメントだけの変更が混ざります。

同じページは、コミットと変更履歴の目的の違いにも触れています。コミットはソースコードの変遷の1歩を記録します。変更履歴の1項目は、複数のコミットにまたがることもある注目すべき差を、利用者に伝わる形で書くものです。

つまりgit-cliffの出力は、完成品ではなく下書きとして扱うのが筋です。役割は次のように分かれます。

くらべる

下書きを作る側と、仕上げる側

機械で再現できる部分

git-cliff

コミットを集めて、区分ごとに並べ、版ごとの節と日付を作ります。同じ入力なら同じ出力になります。

読んで判断する部分

Claude Code

誤分類を見つけ、内部向けの項目を落とし、利用者の目線で1行ずつ書き直します。出力は実行のたびに揺れます。

揺れる側に、毎回の再現性を期待しないことが大事です。Claudeに直させた文面が次の再生成で消えないように、保存の仕方も後半で決めます。

cliff.tomlで、コミットの型をKeep a Changelogの区分に割り当てる

Keep a Changelogが挙げる変更の区分は6つです。Added(新機能)、Changed(既存機能の変更)、Deprecated(近く削除予定)、Removed(削除済み)、Fixed(バグ修正)、Security(脆弱性)です。

git-cliffでは、cliff.tomlの[git]節にあるcommit_parsersが、コミットを区分に割り当てる部分です。git-cliff --initを実行すると既定のcliff.tomlが書き出されます。入門ページによると、既定の設定はConventional Commitsを前提にしていて、featやfix、docsなどで区分を作ります。

commit_parsersの書き方

commit_parsersの各要素は、コミットのどこを見るかを指定します。設定ページに載っている例を表にします。

指定動き
{ message = "^feat", group = "Features" }動き1行目(description)がfeatで始まるものを区分に入れる
{ body = ".*security", group = "Security" }動き本文にsecurityを含むものを区分に入れる
{ footer = "^changelog: ?ignore", skip = true }動きフッターに印があるコミットを除外する
{ message = "^revert", skip = true }動きrevertで始まるコミットを除外する

continue = trueを付けると、同じコミットに後続のパーサーも適用されます。付けない場合は、最初に当たった要素で処理が終わります。順番が意味を持つため、広い条件(^.*のような全部拾う指定)は最後に置きます。

Claudeにcliff.tomlの下書きを書かせる

読み込みの前提になる情報は、リポジトリ側に置きます。CLAUDE.mdはセッションの開始時に毎回読み込まれます。コミット規約と変更履歴の方針を書いておけば、cliff.tomlを書かせるときも、あとで文面を直させるときも同じ前提で動きます。

## CHANGELOG の方針
- 形式は Keep a Changelog。区分は Added / Changed / Deprecated / Removed / Fixed / Security
- 生成は `git cliff`。cliff.toml の `commit_parsers` で区分を決める
- コミットは Conventional Commits(feat / fix / docs / refactor / perf / test / chore)
- docs・test・chore・ci は CHANGELOG に出さない(skip)
- 項目は利用者が見える変化を現在形で 1 文。内部のクラス名や関数名は書かない
- `git cliff` を実行してよいが、タグ付けとプッシュはしない

そのうえで、次のように依頼します。

cliff.toml を Keep a Changelog の 6 区分に合わせて書いて。
feat は Added、fix は Fixed、perf と refactor は Changed、
docs・test・chore・ci は skip にして。BREAKING CHANGE は落とさないで。

「落とさないで」の指示に対応する設定がprotect_breaking_commits = trueです。これを有効にすると、破壊的変更がどのパーサーのスキップにも巻き込まれなくなります。

Claudeが書く設定の骨格は、たとえば次のような形になります(ドキュメントのキー名に沿った例です。実際の出力ではなく、考え方の見本です)。

[git]
conventional_commits = true
filter_unconventional = false
protect_breaking_commits = true
filter_commits = false
fail_on_unmatched_commit = false
commit_parsers = [
    { body = "(?i)security|CVE-", group = "Security" },
    { message = "^docs", skip = true },
    { message = "^test", skip = true },
    { message = "^chore", skip = true },
    { message = "^ci", skip = true },
    { message = "^feat", group = "Added" },
    { message = "^fix", group = "Fixed" },
    { message = "^(perf|refactor)", group = "Changed" },
]

filter_unconventional = falseにしているのは、Conventional Commitsでない題名のコミットも一度は拾うためです。設定ページには、既定はtrueで、規約外のコミットを除外すると書かれています。履歴に規約外のコミットが多いリポジトリでは、falseにして別の区分で受けないと、変更履歴が空に近くなります。

body = "(?i)security|CVE-"の行は先頭に置いています。パーサーは書いた順に評価されるため、^fixより後ろに置くと、セキュリティ修正のコミットが先にFixedへ入ってしまいます。割り当ての優先順位を決めるのは、設定を書くときの一番の山場です。

同梱のサンプル設定のままだと、どこで誤分類が起きるか

git-cliffのリポジトリには、Keep a Changelog向けの設定例(examples/keepachangelog.toml)があります。そのまま使えば手早く形になりますが、割り当てには癖があります。例のcommit_parsersでは次のようになっています。

コミットの型例の割り当て先
題名がadd・supportで始まる、または<scope>: add例の割り当て先Added
題名がremoveで始まる、または<scope>: remove・<scope>: delete例の割り当て先Removed
testで始まる例の割り当て先Fixed
fixで始まる、または<scope>: fix例の割り当て先Fixed
上のどれにも当たらないもの(^.*)例の割り当て先Changed

ここから、3つの誤分類が起こりえます。

  • テスト追加のコミット(test: ...)がFixedに入ります。利用者にとってバグ修正ではないので、Fixedの項目として並ぶと混乱します。
  • docs:やchore:、ci:も最後の^.*でChangedに落ちます。依存の更新やCI設定の変更が、既存機能の変更として載ります。
  • feat:で始まる題名は、addやsupportで始まっていなければChangedになります。新機能がAddedに入らない場合があります。

例の設定はfeatを直接は扱っていません。Conventional Commitsの型ではなく、動詞で始まる題名を前提にしているためです。自分のリポジトリのコミット規約に合わせて、割り当てを書き直す必要があります。上の「Claudeに書かせた設定」は、この違いを埋める出発点です。

割り当てを書き換える前に、実際の履歴で確かめる

設定を信用する前に、Claudeに実際のコミットを区分に当てはめさせます。git-cliffを動かす前の確認として使えます。

git log --oneline v1.1.0..HEAD | claude -p \
  "各コミットが Keep a Changelog のどの区分になるか表にして。
   CHANGELOG に出さないほうがよいものは理由つきで挙げて"

標準入力をパイプで渡してclaude -pに読ませる使い方は、ワークフロー集に載っている方法です。出てきた表と、git cliffの実際の出力を見比べると、cliff.tomlが拾えていないコミットが分かります。

生成して、差分を読ませる

cliff.tomlができたら、まだ版を切っていない変更だけを標準出力に出します。

git cliff --unreleased --tag 1.2.0

--unreleasedはタグが付いていないコミットだけを処理します。--tag 1.2.0は、実際にタグを作らずに、次の版を1.2.0として見出しを描きます。どちらも使用例のページに載っているオプションです。

ファイルへの書き出し方は2通りあります。

方法効果向く場面
git cliff -o CHANGELOG.md効果出力先のファイルへ書き出す向く場面履歴全体を作り直すとき
git cliff --unreleased --tag 1.2.0 --prepend CHANGELOG.md効果既存ファイルの先頭に新しい版の節を足す向く場面版を切るたびに追記するとき

--prependを使うと、過去の版の節はそのまま残ります。全体を-oで書き出す運用だと、過去の節も毎回生成結果で置き換わります。Claudeに直させた文面を残したいなら、運用は--prependのほうが安全です。使用例のページには、--prependは同じファイルを指す-oとは併用できない、という注記もあります。

Claudeに生成結果を読ませる

下書きができたら、権限を絞った非対話実行で直させます。

git cliff --unreleased --tag 1.2.0 --prepend CHANGELOG.md
claude -p "CHANGELOG.md の先頭の 1.2.0 の節を、CLAUDE.md の方針で見直して。
  誤分類と利用者に関係ない項目を直し、直した理由を一覧で出して" \
  --allowedTools "Read,Edit,Bash(git log *),Bash(git diff *)"

--allowedToolsは、確認なしで使えるツールを指定する引数です。Bash(git log *)のように末尾に*を付けると、そのコマンドで始まる呼び出しだけを許可します。スペースを*の前に入れることが大事で、入れないとgit log-xxxのようなコマンドまで一致します。ここではgit pushやgit tagを許可に入れていません。変更履歴を直すだけの作業に、リリースの権限は要らないためです。

誤分類を見つけたら、直す場所を2つに分ける

Claudeが指摘した誤分類を、そのまま文面で直すだけでは、同じ型の誤分類が次のリリースでも出ます。直す場所の判断は、次の表のとおりです。

症状直す場所理由
chore:の依存更新が毎回載る直す場所cliff.tomlにskip = trueを足す理由同じ型が必ず再発する
特定の接頭辞の題名(WIPなど)が混ざる直す場所cliff.tomlにskipの条件を足す理由型で見分けられる
区分に入るが、書き方が内部向け直す場所文面を直す理由コミット側の問題で、パーサーでは直せない
1つの変更が複数コミットに割れて重複する直す場所文面を直す(統合する)理由履歴が割れているのは、その版だけの事情
featのはずがChangedに入る直す場所cliff.tomlの順番や条件を直す理由順番の問題は、必ず再発する

文面の書き換えは、その版だけの対処です。cliff.tomlの側の修正は、次の版以降すべてに効きます。Claudeに依頼するときは「設定を直したほうがよい誤分類」と「今回の文面だけ直すもの」を分けて答えさせると、後者を設定に混ぜずに済みます。

直した設定が意図どおりかを調べるには、fail_on_unmatched_commitが役に立ちます。これをtrueにすると、commit_parsersのどれにも当たらないコミットがあるとgit-cliffが失敗します。明示的にskipしたコミットは、この数に入りません。分類漏れを気づかないまま通さないための設定です。

落とし穴

`-o`で全体を作り直すと、手で直した文面が消える

git cliff -o CHANGELOG.mdは、履歴全体から変更履歴を作って出力先に書きます。Claudeや人が過去の版の節を書き直していても、生成結果で置き換わります。書き直しを残す場合は、版を切るときだけ--prependで足していく運用にします。

利用者向けの文に書き換える

区分の割り当てが直ったら、次は1行ごとの文面です。コミットの題名は、開発者が履歴を読むために書いた文です。Keep a Changelogの原則どおり、変更履歴は人間のために書きます。

前と後

コミット題名から、利用者向けの1行へ

BEFORE

git-cliffの出力(例)

  • Add oauth2 token refresh handler
  • Nil check in export worker
AFTER

書き換えの例

  • ログインが長時間続いても、再ログインを求められなくなりました(トークンを自動更新します)
  • CSV出力が、空のデータで失敗することがある問題を修正しました

上は例示です。実際の出力はリポジトリの履歴で変わります。変わっているのは3点です。内部の実装名(oauth2 token refresh handler)が、利用者が体験する変化に置き換わっています。「何が起きていたか」が文になっています。英語の題名が、読者の言語の文になっています。

Claudeに頼むときは、次のように条件を固定します。

1.2.0 の各項目を、CLAUDE.md の方針で書き換えて。
- 元のコミットの意味を変えない。コミット本文に書いていない動作は足さない
- 判断できない項目は、書き換えずに「要確認」と注記して残す
- 書き換えた項目と元のコミットの対応を一覧にする

最後の対応表が、レビューの手がかりになります。書き換えは、コミットに書かれた事実を言い換える作業であって、事実を足す作業ではありません。コミット本文にない動作が文に入っていたら、Claudeの推測が混ざっています。差分を読むのは人の仕事として残します。

安全面: replace_commandとBashの許可

cliff.tomlは設定ファイルですが、実行されるコマンドを書ける場所があります。commit_preprocessorsとpostprocessorsのreplace_commandは、一致した文字列を外部のコマンドに渡します。公式の例にはpandocや、シェルのrevを使う例が載っています。

Claudeが書いたcliff.tomlにreplace_commandが入っていたら、中身を読んでから実行します。生成だけをして外部コマンドを動かしたくないときは、--no-execを付けます。使用例のページでは、プリ・ポストプロセッサに定義されたコマンドの実行を省略するオプションです。

Claudeに許可するコマンドは、変更履歴の作業に必要な範囲に絞ります。Bash(git cliff *)を許可に入れておけば、生成までは確認なしで進みます。タグ付けやプッシュは、許可に入れないでおきます。

よくあるつまずき

git cliffが動かずgit-cliffだけが動く

入門ページによると、git cliffとgit-cliffはほとんどの環境で同じように使えます。ただしNPMで入れた場合はgit-cliffを使う必要があります。CLAUDE.mdやworkflowに書くコマンドも、入れ方に合わせます。

PR単位の情報が出ない

PRのタイトルや番号、ラベルをCHANGELOGに使いたい場合は、リモート(GitHubなど)から取得する設定になります。FAQは、PR運用ではsquashマージを勧めています。マージコミットやリベースマージだと、PR単位の情報が1つのコミットに結び付かないことがあるためです。履歴の形が悪いと、Claudeに直させても限界があります。

古いタグが版として出てこない

tag_patternの正規表現に合わないタグは、版の区切りとして扱われません。既定の例はv[0-9].*です。タグ名がrelease-2024-01のような形式なら、パターンを合わせます。

まとめ

git-cliffは変更履歴の下書きを再現可能に作り、Claude Codeはその下書きを読んで直す役目です。型で見分けられる誤分類はcliff.tomlのcommit_parsersへ戻し、その版だけの言い回しは文面で直します。この線引きができると、リリースのたびに直す量が減っていきます。

Keep a Changelogのサイトが「git logをそのまま貼らない」と書いているのは、下書きを人が読む工程を省くなという意味にも取れます。Claudeに任せるのは、その工程の最初の読みまでです。最終的に載せる文面の責任は、リリースを出す人に残ります。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. git-cliff.org/docs
  2. git-cliff.org/docs/configuration/git
  3. git-cliff.org/docs/usage/examples
  4. github.com/orhun/git-cliff/blob/main/examples/keepachangelog.toml
  5. keepachangelog.com/en/1.1.0
  6. code.claude.com/docs/en/common-workflows
  7. code.claude.com/docs/en/headless
  8. code.claude.com/docs/en/permissions
この記事を共有:XはてブLinkedIn