🐯

【Git】squash fixup 複数のコミットを1つにまとめる

2024/11/19に公開

Gitで作業をしていると、手違いで同じ内容のコミットが複数できてしまうことや、こまめにコミットしたものをまとめたいという場面が出てきます。
この記事では、そんなときどのように整理すればよいか、具体的なケースを挙げて手順を説明します。

前提条件

  • Gitの基本的な操作ができること
  • コマンドラインの基本的な操作ができること

Case1: 同じ内容のコミットが重複している場合

事例

GitHubのコミット履歴やSourceTreeなどのGUIツールで確認すると、同じ内容のコミットが2つ存在しているのを発見した。

解決手順

このような場合、squashを使用してコミットを統合し、履歴をクリーンにすることができます。

1. 対象のブランチにいることを確認

git branch
git checkout your-branch-name  # 必要に応じて対象のブランチへ移動

2. 直近の2つのコミットをまとめる

このコマンドの前に念のためgit logで確認しておくと安心です。
まとめようとしているコミットが認識と合っているなら以下のコマンドを実行しましょう。

git rebase -i HEAD~2

3. vimエディタで操作

  • iキーを押して編集モードに入る
  • 2行目のpicksquash(またはs)に変更
pick abc1234 first commit message
squash def5678 second commit message
  • Escキーを押して編集モードを終了
  • :wqと入力してEnterキーを押す(保存して終了)

4. コミットメッセージの編集

新しいエディタ画面が開き、統合後のコミットメッセージを編集できます。

  • 両方のコミットメッセージが表示されます
  • 不要な行は#でコメントアウトするか削除します
  • 新しいメッセージを作成または既存のメッセージを編集します
  • 保存して終了(:wq

5. リモートに反映

git push -f origin your-branch-name

このコマンドは、ローカルのyour-branch-nameブランチを、リモートリポジトリ(origin)の同名ブランチに強制的に上書きするコマンドです。
-fオプションは--forceの略で、リモートブランチの履歴を無視して、ローカルブランチの内容で上書きします。

💡 Tips:squashコマンドについて

squashはこの後紹介するfixupと似ていますが、以下の点が異なります。

  • squash: 両方のコミットメッセージを参照しながら、新しいメッセージを作成できます
  • fixup: 後のコミットメッセージは破棄され、最初のメッセージのみが残ります

重複したコミットをまとめる際は、両方のメッセージを確認しながら適切なメッセージを作成できるsquashが便利です。

Case2: 複数の作業コミットを1つにまとめる

事例

開発中にこまめにコミットをしたため、fix: タイプミスの修正fix: 軽微なバグ修正...のように細々したコミットがいくつもある。
プルリクエスト前にきれいにまとめたい。

💡wip: ? fix: ?: コミットメッセージのプレフィックスについては以前ちょこっと書いたので、ここにそっと置いておく👀
https://zenn.dev/n_haru2/articles/b0c8deffa1a5df

手順

この操作は、他のメンバーへ影響を与えたり、せっかく時間をかけて書いた変更が失われたりする恐れがあります。慣れていない方や不安な方は 手順1 作業用のブランチを生やして練習するところから始めることをおすすめします。

1. まとめる作業用の新しいブランチを作成

git checkout -b refactor/clean-commits  # お好きなブランチ名

2. まとめたいコミットの数を確認

最新のコミットからまとめたいコミットまでの数を数えます。

git log

3. リベースを実行

先ほど数えた数を入れて、rebaseコマンドを実行します。

git rebase -i HEAD~まとめたいコミット数

4. エディタで編集

  • 残したいコミット(通常は最初のコミット)はpickのままにする
  • それ以外のコミット(まとめたいコミット)をfixupに変更
  • 保存して終了(Escキー → :wq → Enter)

5. リモートに反映

git push -f origin refactor/clean-commits  # あなたが作業しているブランチ名

作業例

まず、git logを実行すると以下のように表示されます。
見え方は多少違うかもしれません。だいたい最新のコミットが一番下に来ます。

$ git log
commit jkl3456 (HEAD -> feature/new-function)
Author: Your Name <your.email@example.com>
Date:   Tue Nov 19 10:00:00 2024 +0900

    refactor: コードの整理

commit ghi9012
Author: Your Name <your.email@example.com>
Date:   Tue Nov 19 09:30:00 2024 +0900

    fix: 軽微なバグ修正

commit def5678
Author: Your Name <your.email@example.com>
Date:   Tue Nov 19 09:00:00 2024 +0900

    fix: タイプミスの修正

commit abc1234
Author: Your Name <your.email@example.com>
Date:   Tue Nov 19 08:30:00 2024 +0900

    feat: 新機能の追加

:

ここから抜け出せない場合はqを押すと抜けられます。

上から順に最新のコミットが表示されるので、まとめたいコミットを数えます。
今回は4つのコミットをまとめたいので、コマンドは以下のようにしてみます。

git rebase -i HEAD~4

実行。

すると、エディタが開いて以下のような画面が表示されます:

pick abc1234 feat: 新機能の追加
pick def5678 fix: タイプミスの修正
pick ghi9012 fix: 軽微なバグ修正
pick jkl3456 refactor: コードの整理

# Rebase xxxx..xxxx onto xxxx
#
# Commands:
# p, pick <commit> = use commit as is
# f, fixup <commit> = like "squash", but discard this commit's log message
# ...(他のコマンドの説明)

今回は最新のコミットに、それ以外をまとめて1つにしたいので、
以下のように編集します:

pick abc1234 feat: 新機能の追加
fixup def5678 fix: タイプミスの修正
fixup ghi9012 fix: 軽微なバグ修正
fixup jkl3456 refactor: コードの整理

残したい最新のコミットはpickのまま、それ以外のまとめたいものをpickからfixupにしました。
あとは手順でお伝えした通り、保存して終了⇒リモートへ反映、で完了です。

💡 Tips:

  • git logの結果は最新のコミットが下に来ます。
  • エディタで表示される順番はgit logとは逆です。最新のコミットが上に来るのでよく確認してから操作しましょう。
  • コミットをまとめるときは、残したいメッセージのコミット(通常は一番新しいコミット)をpickにします
    • 上記例では「feat: 新機能の追加」が一番大きく新しい変更なので、これをpickにしています
    • その後の細かい修正コミットなどはfixupでまとめればokです。

🗣 Q&A

Q1: VSCodeのGitのところを見ると「Branchの発行」ボタンが青く光ってる。なぜ?

A: git push -f(強制プッシュ)を行った後たまに起こる現象です。VSCodeがブランチの状態を誤認識している状態かもしれません。念のためGitHubのほうでも確認したり、状態を更新したり、状況を調査してみましょう。

以下の対処方法を順番に試してみてください:

1. GitHubで確認

  • GitHubでブランチが存在するか確認
  • コミットが正しくプッシュされているか確認

🤔 ここが問題なさそうなら...

2. VSCode上での解決方法

  • コマンドパレットを開く(Windows: Ctrl+Shift+P / Mac: Cmd+Shift+P
  • Git: Refresh StatusまたはGitLens: Git Statusと打つ、コマンド見つけたらクリックして実行
  • これによりGitの状態が更新されます

3. それでも解決しない場合

この操作は他のメンバーに影響を与えません。変更が失われることもありません。
安心して落ち着いて確認してみましょう。

# リモートの最新情報を取得
git fetch --all

# ブランチの追跡状態を確認
git branch -vv

# 必要に応じて追跡関係を設定
git branch --set-upstream-to=origin/[ブランチ名] [ブランチ名]

ここまで来れば、だいたいどこかの段階で解決していると思います。
1の段階がクリアしているなら特に大きな問題はないのであまり気にしなくて大丈夫です。

Q2: リベース作業の途中でわからなくなった。やっぱりこの操作やめたい。

A: リベース作業で迷った場合は、以下のコマンドで作業を中止できます。
Gitの操作は、もし間違えてしまっても落ち着いて対処すれば取り戻せることが多いので安心してください。ただ、こういったときのために事前にバックアップをとっておくと、より安心ですね。

git rebase --abort  # 作業中止

コミットをまとめるためにバックアップを取りたいときは

git branch backup/feature-branch  # バックアップ作成

💡ポイント

  • git push -fは強制的にプッシュするコマンドです。実行前にはチェックを行い、他の開発者と共有しているブランチでは(そうでなくとも)細心の注意を払いましょう。
  • チーム開発では、まとめる作業を行う前に、なるべく他のメンバーや管理者に通知しておくことをお勧めします。
  • コミットをまとめる前に、最新からいくつか、どれをどこにマージしたいのか、対象やその範囲を正確に確認しましょう。
  • 慣れないうちは、作業用のブランチを作成してから試すと、何かミスがあっても影響が最小限に済み、バックアップも取れて安心です。

まとめ

VSCodeなどのGUIツールでも同様の操作が可能ですが、意図せぬ操作を実行してしまう可能性もあります。何をしているのか理解するためにも、はじめはコマンドラインで操作を行い、慣れてきたら便利なツールも使うように移行していくと、対応できる幅が広がり、何かあってもより柔軟に対応できるようになります。

コミットをまとめる操作は、履歴をクリーンに保つために有用なスキルです。特にチーム開発ではレビューのしやすさや履歴の可読性向上にもつながるため、ぜひ練習して習得してみてください。

Discussion