はじめに
コードレビューで指摘を受けて修正を重ねていくと、「修正」「再修正」「typo修正」といったコミットが積み重なり、PRの履歴が煩雑になってしまった経験はありませんか?
今回は、git fixupコミットを活用して、きれいなコミット履歴を保ちながら効率的にコードレビューを進める方法をご紹介します。
fixupコミットとは?
fixupコミットは、既存のコミットに対する修正を一時的に別コミットとして作成し、後でrebaseによって元のコミットに統合するための仕組みです。
通常のコミット:
feat: ユーザー認証機能を追加
fix: 認証エラーを修正 ← レビュー指摘対応
fix: typoを修正 ← さらに修正
fix: エラーメッセージを改善 ← さらにさらに...
fixupコミットを使った場合:
feat: ユーザー認証機能を追加
fixup! feat: ユーザー認証機能を追加 ← レビュー対応が明確!
最終的にrebaseすると:
feat: ユーザー認証機能を追加 ← 1つのきれいなコミットに!
使うことによるメリット
1. コミット履歴が綺麗になる
- 最終的なマージ時には、論理的にまとまった単位のコミットだけが残る
- 将来的にgit blameやgit logで履歴を追いやすい
- リバート時も対象が明確
2. レビューが格段にしやすくなる
- どのコミットに対する修正なのかが一目瞭然
- レビュアーは「前回の指摘がどう修正されたか」を追いやすい
- 差分の文脈が明確になる
3. 開発効率の向上
- 一時的な修正コミットを気にせず作業できる
- 後から整理できるので、思考を中断せずに修正を進められる
基本的な使い方
Step 1: fixupコミットの作成
# 修正したいコミットのハッシュを確認
$ git log --oneline
abc1234 feat: ユーザー認証機能を追加
def5678 docs: READMEを更新
# 修正を行った後、fixupコミットとして作成
$ git add .
$ git commit --fixup=abc1234
# または
$ git commit --fixup=HEAD~1 # 1つ前のコミットを修正する場合
Step 2: rebaseで統合
# 統合したいコミットの親まで遡ってrebase
$ git rebase -i --autosquash HEAD~3
# または、ブランチの起点から
$ git rebase -i --autosquash origin/main
--autosquashオプションを使うと、fixupコミットが自動的に適切な位置に配置されます。
ただこんな経験はありませんか?
「fixupコミットをrebaseせずにマージしてしまう」
私が頻繁に行なってしまうミスです...
(つい他の作業に移るとfixup!コミットをrebaseするのを忘れてしまう、あるある)
このままマージしてしまうと、fixup!というプレフィックスがついているコミットが残ってしまい、コミット履歴が汚れてしまいます。
そこで、fixup!コメントがある際に、コメントをPR上に残してくれる workflow を導入しました!
GitHub Actionsでチェック
以下のワークフローを.github/workflows/check-fixup.ymlとして追加:
name: Check Fixup Commits
on:
pull_request:
types: [opened, synchronize]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
check-fixup:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
with:
fetch-depth: 0
- name: Check for fixup! commits
id: check_fixup
run: |
git fetch origin ${{ github.base_ref }}
if git log --oneline origin/${{ github.base_ref }}..HEAD | grep -qE "^.*fixup!"; then
echo "has_fixup=true" >> $GITHUB_OUTPUT
else
echo "has_fixup=false" >> $GITHUB_OUTPUT
fi
- name: Find existing comment
id: find_comment
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3.1.0
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: '<!-- fixup-commits-check -->'
- name: Create or update comment
if: steps.check_fixup.outputs.has_fixup == 'true'
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
with:
issue-number: ${{ github.event.pull_request.number }}
comment-id: ${{ steps.find_comment.outputs.comment-id }}
edit-mode: replace
body: |
<!-- fixup-commits-check -->
:warning: **注意:** このPRには `fixup!` コミットが含まれています。
マージする前にスカッシュ(squash)してください。
- name: Delete comment when resolved
if: steps.check_fixup.outputs.has_fixup == 'false' && steps.find_comment.outputs.comment-id != ''
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
with:
script: |
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: ${{ steps.find_comment.outputs.comment-id }}
})
このワークフローの嬉しいポイント
1. マージはブロックしない
- 警告コメント として実装しているため、意図的にfixup!コミットを残してマージしたい場合にも対応できる
2. 視覚的なわかりやすさ
- PR上に自動でコメントが表示されるため、PR作成者・レビュアー共にfixup!コミットの存在を一目で認識可能
- マージ前の最終チェックリストとして機能し、「あ、rebase忘れてた!」を防げる(かも)
- また、解消されたらコメントが自動で削除されるのも嬉しい点です
まとめ
fixupコミットを活用することで:
きれいなgit履歴 - 論理的にまとまったコミットだけが残る
効率的なレビュー - 修正箇所が明確で追いやすい
チーム全体の生産性向上 - コード品質と開発速度の両立
最初は慣れが必要ですが、一度ワークフローに組み込めば、もう元には戻れません!
GitHub Actionsによる自動チェックも導入することで、チーム全体で安心してfixupコミットを活用できます。
ぜひ導入してみてください!!!
