はじめに
開発中、"fix", "修正", "debug" といった、内容のわかりにくいコミットメッセージでブランチを埋めてしまった経験が一度や二度はあるのではないでしょうか?
普段はちゃんと気を付けていても、切羽詰まっているシーンでは疎かになってしまう人もいるのではと思います。
私もつい最近1つのブランチをカオス状態にしてしまい、なんとか修正したところなので、その際の手順を共有したいと思います。
また、コミット履歴を操作する系のコマンドをいくつか紹介していますので、ぜひご活用ください。
発生したカオス
作業中、以下のような状況に陥りました。
- 作業ブランチ (
issue-XXX):- 途中まで丁寧に進めていたが、他開発者のマージが重なりコンフリクト多発
- そのためとりあえず検証用ブランチを作ってこちらは放置
- 検証用ブランチ (
issue-XXX-for-test):-
issue-XXXをベースに作り、とりあえずmasterとのコンフリクトを解消 - stagingへのデバッグ用に「debug」「debug2」...「debug20」という中身のわからないコミットを量産
-
この2つをマージしようにも、履歴が複雑で「何が正しいコードなのか」を追うのが困難な状態になりました。
解決策:第3のブランチで「いいとこ取り」をする
最初はissue-XXX(先に使っていたブランチ)を最新状態にしてPR作成しようと思っていました。しかし、よく考えると最初の方のコミットで行った変更を後の方でやっぱりやめた、みたいな部分も割とあったので、何かいい方法はないかなとGitHub Copilotに相談してみました。
余談ですが、最近はGit操作を結構な割合でCopilotに任せています。
複数ファイルをaddする場合にパスから入力するのが面倒なので、GUI上で選択して「これとこれとこれをaddして、"〇〇"っていうメッセージでcommitして」というように使うことが多いです。
すると、無理に既存ブランチを直すのではなく、「最新のmasterから、成果物だけを抽出したクリーンなブランチ」を新調するべきだとのアドバイスが。やり方も殆どCopilotに教えてもらいながら実行しました。
1. クリーンな土台を作る
masterから新たなブランチを切ります。
git checkout master
git pull origin master
git checkout -b issue-XXX-clean
2. 履歴を捨てて「中身だけ」持ってくる git merge --squash
カオス化したブランチの変更内容を、コミット履歴を無視して現在のブランチにぶつけます。
git merge --squash issue-XXX-for-test
--squashと--no-commit --no-ffの違い
- 前者は、
issue-XXX-for-testのコミット履歴を残しません。 - 後者は、
issue-XXX-for-testのコミット履歴を残します。
本記事の用途ならどちらも使えるのですが、ここでは混乱したブランチを刷新することを目的としているので、前者をメインで紹介しています。
これで、ローカルのissue-XXX-cleanには最新成果物が残っており、エディタ上ではmasterと成果物すべての差分が見られるという状態になります。
3. きれいな履歴を作り直す git add -p
ここから、ひとまとまりになった成果物を、適切な単位に分けてコミットしていきます。
- ファイル単位でコミットするもの:いつも通り、ファイル名を指定してadd&commit
- ファイル内でも複数のコミットに分けたいもの:
git add -pを使う
ここでは、主に 2. の方法を見ていきます。
Git Bashで実行
私が最初に試した方法がこれです。
git add -p resources/templates/home.html
これを実行すると、hunkと呼ばれる、ファイル内の差分の一部が表示されます。
最初は自分の意図とは関係なく決められたhunkが提示されます。
それが意図した分割単位と同じならいいのですが、異なる場合はsでさらに分割するか、eで編集することになります。
Vimでこの編集をやってみたのですが、これが結構苦行でして、、
- 変更箇所の多いファイルだと、コミットしたい変更箇所を見つけるのに一苦労
- 不要な行を消すため、100行以上もBackSpaceを押し続ける
- やっと終わったと思ったら、スペースの位置が合わないとかで保存できず、1からやり直し
などなど。
そんなわけですので、この2026年に生きるみなさんには次の方法をお勧めします。
VScodeのGUIで実行
普段VScodeでコーディングしているにもかかわらず、Git操作にはあまりVScodeのGUIを活用してきませんでした。
が、この記事を書くにあたって、git add -pについてGoogle Geminiと対話しているときに知って試したところ、「今までの私の苦労は何?」となるくらいには便利だったので、まだ使ったことのない人にはぜひ試してもらいたいです。
<行単位でコミットする方法>
VScodeの差分ビュー(Diff View)を使えば、マウス操作だけで簡単にコミットを整理できます。
- ソース管理タブ(Source Control)からファイルをダブルクリックして差分を開く
- コミットしたい行だけをドラッグして選択
- 右クリック > 選択した範囲をステージング(Stage Selected Ranges)をクリック
これだけで、選んだ行だけがstageされます!
ファイルAとBから「機能Xの修正」だけをコミットし、他は次のコミットに回す、といった操作がGUIで簡単にできますね。
失敗したときに使えるコマンド
コミットを取り消す git reset
git reset HEAD~1
ファイルの中身はそのままで、直前のコミットだけを取り消す操作です。
これを使うと「addする前の状態」に戻るので、改めて適切な単位でコミットしなおすことができます。
HEAD~xのxには任意の数字が入り、最新x個のコミットに対して操作を行うことができます。
リモートにPushした後にresetすると、他の開発者が困る場合があるので注意。
また、git resetのオプションを混同しないよう注意が必要です。
--mixed:デフォルト。コミットは消えるが、ファイルの中身は残る
--soft:コミットは消えるが、ファイルはステージング済み(addされた状態)で残る
--hard:要注意! コミットもファイルへの変更も全部削除される
コミットをひとまとめにする
①任意のコミットをまとめる git rebase -i
おそらく主流と思われる方法です。
例えば、最新から見て5個目と4個目のコミットをひとまとめにしたいといったときに使えます。
git rebase -i HEAD~5
というように開始します。
この後の操作については解説してくれている記事がたくさんあるので、たとえばこちらの記事などを参照されるとわかりやすいと思います。
②最新からいくつかのコミットをまとめる
①のgit rebase -iは柔軟に使えますが、エディタ操作が少し面倒です。
特に、最新の数個のコミットをまとめたいだけの場合は、以下のような方法が手軽です。
(使用時には注意点がありますので、黄色の!を必ずお読みください)
例えば、最新から5つのコミットを全部1つにまとめたいといった場合に使えます。
先ほど紹介したgit reset --softで5つのコミットをステージに戻し、再度コミットします。
git reset --soft HEAD~5
git commit -m "5つのコミットを綺麗に1つにまとめました"
すると結果的に、直近5つのコミットが1つのcommitにまとまったように見えます。
ここで、git reset --softの操作を行う前に他の変更がaddされていた場合、その変更も一緒のcommitにまとまってしまいますので要注意です。
また、コミットハッシュ(ID)が変わりますので、Push後に使う場合は検討が必要です。
まとめ
複雑な履歴を抱えたブランチを無理に修復しようとするよりも、新しいものを作り直す方が速いですね。
- 履歴をまとめるなら
merge --squash - 細かくaddするなら
git add -p、これをラクするならVScodeの「選択範囲をステージング」 - 間違えたら
git reset HEAD~1
Git操作は、慣れないと「なんとなく取り返しつかなさそうで怖い」と思ってしまいがちですが、意外といろいろなコマンドがあって柔軟に操作できたりするようです。
まだまだ知らないコマンドの方が多いですが、自信を持って最適なコマンドを打てるように精進していきます。