📌

【Git】Vimでいくつかのコミットを1つにまとめる方法

2024/12/20に公開

はじめに

Gitで複数のコミットをまとめて、コミットメッセージを変更するように指示されたときに調べても操作方法ががよくわからず詰んだので、私と同じように詰んでる人がいるんじゃないかと思い、記事に残します。

この2点について説明していきたいと思います✨

使用マシン:MacBook Air
OS:Sonoma 14.7.1
使用エディタ:IntelliJ IDEA
git:2.39.3 (Apple Git-146)

Gitで「隣り合ったコミット」をまとめる方法

下準備として何個前のコミットをまとめるのかを調べます。
私はGitHubでコミットを数えました。

1. インタラクティブリベースを開始する

まず、順番を変更したいコミットが含まれているブランチでインタラクティブリベースを開始します。

git rebase -i HEAD~5

HEAD~の後ろの数字が何個前のコミットまで出すかの指定なのでHEAD~55の部分を先ほど調べた「何個前のコミットまで戻りたいか」の数字に変更し、そのコードを「ターミナル」に貼り付けてenterを押します。
私はIntelliJを使用しているのでIntelliJの「ターミナル」に貼り付けました。

2. インタラクティブなエディタが開かれる

上記のコマンドを実行すると、エディタが開きます。
エディタには、対象となるコミットが一覧表示され、次のような内容が表示されます。

pick abc1234 Commit 1のコミットメッセージ
pick def5678 Commit 2のコミットメッセージ
pick ghi9012 Commit 3のコミットメッセージ
pick fej5428 Commit 4のコミットメッセージ
pick rdk8168 Commit 5のコミットメッセージ

隣り合っていないコミットをまとめたいかたはここから3. コミットの順番を変更するに飛んでください。

3. エディタでコミットの選択

編集をするにはまず半角英数入力に切り替えてからiを押して-- INSERT --状態にし、矢印キーを使って編集したい場所まで移動します。
ここで、まとめたいコミットの最初のものpick のままにし、それ以降のまとめたいコミットsquash または s に変更します。
例えば、Commit 3Commit 4Commit 2 にまとめたい場合は、次のようにします。

pick abc1234 Commit 1のコミットメッセージ
pick def5678 Commit 2のコミットメッセージ
squash ghi9012 Commit 3のコミットメッセージ
squash fej5428 Commit 4のコミットメッセージ
pick rdk8168 Commit 5のコミットメッセージ

または

pick abc1234 Commit 1のコミットメッセージ
pick def5678 Commit 2のコミットメッセージ
s ghi9012 Commit 3のコミットメッセージ
s fej5428 Commit 4のコミットメッセージ
pick rdk8168 Commit 5のコミットメッセージ

これで、Commit 3Commit 4Commit 2 に統合されます。

4. 編集を保存して閉じる

編集を保存して閉じるには-- INSERT --モードを解除しなければいけません。
-- INSERT --モードの解除はescキーを押します。
そして:wqを押すことで閉じることができます。

5. エディタでのコミットメッセージの修正

squash を使用すると、まとめられたコミットのメッセージを編集するためのエディタが再び開きます。ここで、コミットメッセージを修正することができます。

エディタに表示される内容は、次のような感じです。

# This is a combination of 3 commits.
# The first commit's message is:
Commit 2のコミットメッセージ

# The 2nd commit's message is:
Commit 3のコミットメッセージ

# The 3rd commit's message is:
Commit 4のコミットメッセージ

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.

コミットメッセージを修正するにはまた半角英数入力に切り替えてからiを押し-- INSERT --状態にします。

全部消して書き直すには# with '#' will be ignored, and an empty message aborts the commit.の次の空白行まで移動し、最初の# This is a combination of 3 commits.まで削除して書き換えます。
書き換えが完了したら先程と同様にescキーを押し-- INSERT --モードの解除をします。
そして:wqと入力しenterを押すことで閉じることができます。
:wqと入力するときは半角にすることをお忘れなく🙏

6. リベースの完了

リベースが正常に完了すると、指定した順番でコミットが適用されます。
リベースが成功したら、リモートブランチに変更をプッシュする場合は、強制プッシュが必要な可能性があります。

git push --force

私は1人で作業していたので上記のコマンドで対応したのですが、調べるとgit push --forceは非常に強力なコマンドなのでいくつかの注意点があります。

git push --forceの注意点

  1. 履歴の上書き
    --force を使うと、リモートリポジトリの履歴が上書きされます。これにより、他の開発者が行ったコミットが消えてしまうことがあります。特に、複数人で作業している場合、履歴が失われることで他のメンバーが困惑する可能性があります。

  2. 共同作業者への影響
    他の開発者がリモートリポジトリからクローンをしていたり、リモートブランチをフェッチしている場合、--force によってリモート履歴が変更されると、彼らのローカルリポジトリの状態が壊れることがあります。この場合、他の開発者が再度リモートリポジトリを取得し直す必要が出てきたり、手動での修正が必要になります。

  3. マージコミットの失われるリスク
    マージコミットを含んでいる場合、そのマージが --force で上書きされることがあります。これにより、複数のブランチが統合された履歴が消えてしまうことになります。特に、マージに関わるコードやレビュー内容が失われる可能性があるため、慎重に行う必要があります。

  4. コミットの整合性の喪失
    --force を使って履歴を変更すると、その後の履歴が不整合になることがあります。これにより、コードの進行状況やリリースの追跡が難しくなり、特に長期的な開発において問題が発生する可能性があります。

  5. 本番環境への影響
    特にデプロイ用のリモートブランチ(例:main や master)に対して --force を使用することは非常に危険です。もし、本番環境にリリースされているコードの履歴が上書きされると、予期しないバグや問題が発生する可能性があります。チーム内で慎重に運用する必要があります。

  6. レビューやCI/CDとの整合性
    GitHub などでコードレビューや CI/CD パイプラインが動いている場合、--force を使うことでその履歴に基づいたレビューやテスト結果が無効になることがあります。これによって、再度レビューやテストをやり直す必要が出てくることがあります。

代替案として考えられるもの

git push --force-with-lease origin <ブランチ名>

--force よりも安全な方法として --force-with-lease を使う方法があります。このオプションは、リモートリポジトリの状態が自分が最後にフェッチした時点と一致している場合にのみ強制的に push を行います。これにより、他の開発者の変更を上書きするリスクを減らせます。

git push --forceよりもgit push --force-with-leaseを使用するほうが良さそうです🙏

Gitで「隣り合っていないコミット」をまとめる方法

途中までは「複数のコミット」をまとめる方法と同じです!
下準備として何個前のコミットをまとめるのかを調べます。

1. インタラクティブリベースを開始する

まず、順番を変更したいコミットが含まれているブランチでインタラクティブリベースを開始します。

git rebase -i HEAD~5

HEAD~の後ろの数字が何個前のコミットまで出すかの指定なのでHEAD~55の部分を先ほど調べた「何個前のコミットまで戻りたいか」の数字に変更し、そのコードを「ターミナル」に貼り付けてenterを押します。
私はIntelliJを使用しているのでIntelliJの「ターミナル」に貼り付けました。

2. インタラクティブなエディタが開かれる

上記のコマンドを実行すると、エディタが開きます。
エディタには、対象となるコミットが一覧表示され、次のような内容が表示されます。

pick abc1234 Commit 1のコミットメッセージ
pick def5678 Commit 2のコミットメッセージ
pick ghi9012 Commit 3のコミットメッセージ
pick fej5428 Commit 4のコミットメッセージ
pick rdk8168 Commit 5のコミットメッセージ

3. コミットの順番を変更する

  1. まず半角英数入力に切り替えてからiを押し-- INSERT --状態にします。
  2. 移動させたい行をマウスで選択し、ctrl+cでコピーします。
  3. 挿入したい行の1個上の行の右端まで矢印キーでカーソルを移動し、enterを押して改行したら先ほどコピーしたものをctrl+vでペーストします。
  4. コピーした行の右端までカーソルを移動させ、その行を削除します。

例)
2個目のコミットを4個目と5個目の間に挿入したい場合

  1. まず半角英数入力に切り替えてからiを押し-- INSERT --状態にします。
  2. 移動させたい行をマウスで選択し、ctrl+cでコピーします。
  3. 挿入したい行の1個上の行の右端まで矢印キーでカーソルを移動し、enterを押して改行したら先ほどコピーしたものをctrl+vでペーストします。
  4. コピーした行の右端までカーソルを移動させ、その行を削除します。

以降は「複数のコミット」をまとめる方法の3. エディタでコミットの選択から同じです✨

4. エディタでコミットの選択

編集をするにはまず半角英数入力に切り替えてからiを押して-- INSERT --状態にし、矢印キーを使って編集したい場所まで移動します。
ここで、まとめたいコミットの最初のものpick のままにし、それ以降のまとめたいコミットsquash または s に変更します。
例えば、Commit 3Commit 4Commit 2 にまとめたい場合は、次のようにします。

pick abc1234 Commit 1のコミットメッセージ
pick def5678 Commit 2のコミットメッセージ
squash ghi9012 Commit 3のコミットメッセージ
squash fej5428 Commit 4のコミットメッセージ
pick rdk8168 Commit 5のコミットメッセージ

または

pick abc1234 Commit 1のコミットメッセージ
pick def5678 Commit 2のコミットメッセージ
s ghi9012 Commit 3のコミットメッセージ
s fej5428 Commit 4のコミットメッセージ
pick rdk8168 Commit 5のコミットメッセージ

これで、Commit 3Commit 4Commit 2 に統合されます。

5. 編集を保存して閉じる

編集を保存して閉じるには-- INSERT --モードを解除しなければいけません。
-- INSERT --モードの解除はescキーを押します。
そして:wqを押すことで閉じることができます。

6. エディタでのコミットメッセージの修正

squash を使用すると、まとめられたコミットのメッセージを編集するためのエディタが再び開きます。ここで、コミットメッセージを修正することができます。

エディタに表示される内容は、次のような感じです。

# This is a combination of 3 commits.
# The first commit's message is:
Commit 2のコミットメッセージ

# The 2nd commit's message is:
Commit 3のコミットメッセージ

# The 3rd commit's message is:
Commit 4のコミットメッセージ

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.

コミットメッセージを修正するにはまた半角英数入力に切り替えてからiを押し-- INSERT --状態にします。

全部消して書き直すには# with '#' will be ignored, and an empty message aborts the commit.の次の空白行まで移動し、最初の# This is a combination of 3 commits.まで削除して書き換えます。
書き換えが完了したら先程と同様にescキーを押し-- INSERT --モードの解除をします。
そして:wqと入力しenterを押すことで閉じることができます。
:wqと入力するときは半角にすることをお忘れなく🙏

7. リベースの完了

リベースが正常に完了すると、指定した順番でコミットが適用されます。
リベースが成功したら、リモートブランチに変更をプッシュする場合は、強制プッシュが必要です。

git push --force

私は1人で作業していたので上記のコマンドで対応したのですが、調べるとgit push --forceは非常に強力なコマンドなのでいくつかの注意点があります。

git push --forceの注意点

  1. 履歴の上書き
    --force を使うと、リモートリポジトリの履歴が上書きされます。これにより、他の開発者が行ったコミットが消えてしまうことがあります。特に、複数人で作業している場合、履歴が失われることで他のメンバーが困惑する可能性があります。

  2. 共同作業者への影響
    他の開発者がリモートリポジトリからクローンをしていたり、リモートブランチをフェッチしている場合、--force によってリモート履歴が変更されると、彼らのローカルリポジトリの状態が壊れることがあります。この場合、他の開発者が再度リモートリポジトリを取得し直す必要が出てきたり、手動での修正が必要になります。

  3. マージコミットの失われるリスク
    マージコミットを含んでいる場合、そのマージが --force で上書きされることがあります。これにより、複数のブランチが統合された履歴が消えてしまうことになります。特に、マージに関わるコードやレビュー内容が失われる可能性があるため、慎重に行う必要があります。

  4. コミットの整合性の喪失
    --force を使って履歴を変更すると、その後の履歴が不整合になることがあります。これにより、コードの進行状況やリリースの追跡が難しくなり、特に長期的な開発において問題が発生する可能性があります。

  5. 本番環境への影響
    特にデプロイ用のリモートブランチ(例:main や master)に対して --force を使用することは非常に危険です。もし、本番環境にリリースされているコードの履歴が上書きされると、予期しないバグや問題が発生する可能性があります。チーム内で慎重に運用する必要があります。

  6. レビューやCI/CDとの整合性
    GitHub などでコードレビューや CI/CD パイプラインが動いている場合、--force を使うことでその履歴に基づいたレビューやテスト結果が無効になることがあります。これによって、再度レビューやテストをやり直す必要が出てくることがあります。

代替案として考えられるもの

git push --force-with-lease origin <ブランチ名>

--force よりも安全な方法として --force-with-lease を使う方法があります。このオプションは、リモートリポジトリの状態が自分が最後にフェッチした時点と一致している場合にのみ強制的に push を行います。これにより、他の開発者の変更を上書きするリスクを減らせます。

git push --forceよりもgit push --force-with-leaseを使用するほうが良さそうです🙏

これで以上です✨
問題は解決されましたでしょうか?
それではみなさま、良いGit lifeを🍀

BABY JOB  テックブログ

Discussion