ワークフロー
以下の手順を順番に実行してください。いずれかのステップが失敗した場合は停止し、ユーザーに報告してください。
1. 作業ツリーの評価
git status と git diff(ステージ済みおよび未ステージの両方)を実行し、何が変更されたかを把握します。変更がまったくない場合は、コミットするものがないことをユーザーに伝えて停止します。
2. ダーティなサブモジュールのクリーンアップ
git status に「(modified content)」と表示されたサブモジュールがあるが、ステージ済みや意図したサブモジュールポインタの変更がない場合、それらはネストされたサブサブモジュールであり、通常は再帰的な --remote 更新によってドリフトしたものです。そのようなダーティなサブモジュールごとに、git -C <submodule-path> submodule update --init を実行して、親サブモジュールが期待するコミットにネストされた参照を復元します。クリーンアップ後、git status を再確認してダーティが解消されたことを確認します。ユーザーが明示的に変更しない限り、サブモジュールポインタの変更をステージしないでください。
3. ユーザーが特に指定しない限り、すべてをステージング
ユーザーが特定のファイルを指定しなかった場合は、git add -A ですべての変更(追跡済みおよび未追跡)をステージします。ユーザーが特定のファイルやパスを指定した場合は、それらのみをステージします。
4. リンターと自動修正
コミットする前に、プロジェクトのリンターを検出し、ステージされたファイルに対して自動修正付きで実行します。検出順序:
- 最初にプロジェクトレベルのlintターゲット: Makefileに
lintまたはlint:fixターゲットがある場合、またはpackage.jsonにlintまたはlint:fixスクリプトがある場合は、そちらを優先します。これはプロジェクト独自の規約を尊重します。プロジェクトレベルのターゲットがない場合にのみ、言語ごとの検出に進みます。 - Go(go.modが存在する場合): ステージされた .go ファイルに対して
gofmt -wを実行し、その後go vet ./...を実行します。 - JavaScript/TypeScript(package.jsonが存在する場合): ESLint設定が存在する場合は、ステージされたJS/TSファイルに対して
npx eslint --fixを実行します。Prettierが設定されている場合は、それらのファイルに対してnpx prettier --writeも実行します。 - Python(pyproject.toml、setup.py、またはrequirements.txtが存在する場合): Ruffが設定されている場合は、ステージされた .py ファイルに対して
ruff check --fixとruff formatを実行します。blackがインストールされている場合はフォールバックします。flake8(レポートのみ、自動修正なし)にフォールバックします。 - Rust(Cargo.tomlが存在する場合):
cargo fmtとcargo clippy --fix --allow-dirtyを実行します。 - リンターが検出されない場合: このステップをスキップして次に進みます。
自動修正後、git add -u で変更されたファイルを再ステージします。リンターが自動修正できないエラーを報告した場合は、停止してユーザーに報告してください。既知の lint エラーがある状態でコミットしてはいけません。
リンターが検出されたがバイナリがインストールされていない場合は、ユーザーに警告し、lintなしで続行するか中止するかを尋ねてください。
5. コミットメッセージの生成
ステージされた差分を分析して、Conventional Commitメッセージを生成します。
type(scope): short summary
Optional body with context on what changed and why.
ルール:
- type: feat、fix、refactor、docs、test、chore、ci、perf、style、build のいずれか
- scope: オプションですが推奨 — 影響を受ける主要なパッケージ、モジュール、またはディレクトリから導出します。変更が単一のスコープでは意味をなさないほど多くの領域にわたる場合のみ省略します。
- サマリー行: 小文字、命令形、ピリオドなし、最大72文字。
- 本文: 差分が非自明であり、サマリーだけではレビューアに十分なコンテキストを提供できない場合にのみ追加します。簡潔に保ちます。
- 複数の論理的な変更: type/scopeには主要なものを選び、本文で副次的な変更に言及します。ユーザーが明示的に要求しない限り、複数のコミットに分割しないでください。
- 破壊的変更: BREAKING CHANGE フッターを追加するか、
!サフィックス(例: feat!: ...)を使用します。 - リンターがファイルを自動修正した場合は、本文でその旨に言及します(例: 「gofmtの自動修正を含みます。」)。
6. コミット
git commit を生成されたメッセージで実行します。サマリーのみのメッセージには -m を使用します。本文を含むメッセージの場合は、一時ファイルに書き込んで git commit -F を使用して改行を保持し、その後一時ファイルを削除します。
7. リモートとの同期
現在のブランチ名を特定します。その後:
- ブランチに上流がある場合は、
git pushを実行します。 - ブランチに上流がない場合は、
git push -u origin <branch-name>を実行します。 - リモートが先に進んでいるためにプッシュが拒否された場合は、
git pull --rebaseを実行してから再度プッシュします。リベース中にコンフリクトが発生した場合は、停止して報告してください — マージコンフリクトを自動解決しないでください。
8. 確認
ユーザーに報告します:
- コミットハッシュ(短縮形)
- 使用されたコミットメッセージ
- ブランチ名とリモート同期ステータス
- 変更されたファイル、挿入行、削除行(コミットの
git diff --statから) - どのリンターが実行されたか(または検出されなかったこと)
エッジケース
- リモートが設定されていない: ローカルにコミットし、ユーザーに伝えます。
- デタッチドHEAD: コミットする前に警告し、最初にブランチを作成することを提案します。
- 未追跡ファイルのみ: 通常通りステージしてコミットします。
- .gitignoreを尊重する — ユーザーが明示的に要求しない限り、無視されたファイルを強制追加してはいけません。