積み上げたコミットを整理したくなった時に覚えておきたい或いは思い出したいgit rebaseの手続きを試してみた

gitで細かくしすぎたコミットを纏めたい時に「この場合はどんな手順でやるんだったっけ」と振り替えれるよう、実際に試しながら書いてみました。
2021.06.22

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

PullRequestを出す時、作業用ブランチでコミットを細かく刻みすぎている場合はgit rebaseにてある程度のまとまりにしつつ、合わせてコメントも変更したほうがレビューもし易くなります。ただ、rebaseする時に悩ましいのがコミット範囲指定です。HEAD~xxの指定ですね。

比較的大きめの範囲を指定した上でsquashの対象を絞り込めばよいのですが、大きめの範囲を求めるにもコミット内容の遡りは変わらず必要になります。そしてコミットが大きくなるほどスクロール量も増えます。差分を確認するのも一苦労です。

以下のようなコミットをまとめるのならまだしも、実際は検討すべき項目が数多く上がります。

% git log
commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Author: haoyayoi <xxxxxxxxxxxxxxx@gmail.com>
Date:   Mon Jun 14 14:25:12 2021 +0900

    fix typo

commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Author: haoyayoi <xxxxxxxxxxxxxxx@gmail.com>
Date:   Mon Jun 14 14:23:42 2021 +0900

    fix typo

commit XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Author: haoyayoi <xxxxxxxxxxxxxxx@gmail.com>
Date:   Mon Jun 14 14:21:22 2021 +0900

    fix typo

より快適にrebaseする方法は幾つかあります。「どんな状況でも多分多少はマシになる」というものを幾つかピックアップして試してみました。

GitHub Desktop

squash限定になりますが、これ以上気楽にrebaseできるクライアントはないと思います。

rebase対象にするコミットを範囲選択して右クリック。

squashを選択して実行するのみです。

tig

Terminalだけで済ませたい場合はGitよりもTigの方がよいでしょう。

% brew install tig

Rebase操作を手軽にするため、.tigrcに設定を追加しておきます。

% vim ~/.tigrc
bind main    R !git rebase -i %(commit)
bind diff    R !git rebase -i %(commit)

Rキー一つでRebase操作が開始可能になりました。

Tigを起動してEnterキーを押してコミットログを確認しつつ、対象にしたい最も古いコミットログ上でRを押します。GitHub Desktopと違って、squashかfixupのいずれかを個別に指定します。

squash
コミットそれぞれのコメントを纏められる
fixup
対象コミットからコメントを排除した上で直前コミットへの差分統合を行う

所謂fix typoで無い限りはsquashを選択した上でコメントを再編集したほうがレビュー時に意図が分かりやすくなります。

これをやっておきたいのならrebase

普段の利用頻度が少なければ何が出来るのかも把握しづらいものです。「こんなことをやっておきたい」という時に「それrebaseでできるよ」というものを一部ピックアップしてみました。

  • 作業ブランチ内で既に積み上げたコミットのコメントを変更する
  • 一度に複数追加したファイルを個別のコミットに分けて追加し直したい
  • コミットの順番を入れ替えたい  

作業ブランチ内で既に積み上げたコミットのコメントを変更する

Tigを起動し、編集したいコミットよりも下のコミットにフォーカスを当ててからRを押します。(セキュリティ対策…のコミットが対象)

該当コミットのpickeditに変更して保存します。

Before:

pick zzzzzzzz セキュリティ対策としてDeploy用RoleとCfn用Roleを切り分ける

After:

edit zzzzzzzz セキュリティ対策としてDeploy用RoleとCfn用Roleを切り分ける

すると以下の表示に切り替わります。どうしたらよいものかと慌てそうですが、先ずは指示通りEnterを押します。

You can amend the commit now, with

  git commit --amend

Once you are satisfied with your changes, run

  git rebase --continue
Press Enter to continue

次にqを押してTigを閉じます。そして、ターミナル上に残っている指示に従います。

先ずはコメントの修正。

% git commit --amend

続いてrebase作業の続きです。

% git rebase --continue

これで既に積んでいたコミットのコメント修正が完了しました。複数コミットのコメントを変更したい場合は、同じくrebaseにてそれぞれをeditと変更します。

以下は2つのコミットに対して、コメントを変更してrebaseの継続、をコミット毎に行う例です。

# 最初のeditコミット
% git commit --amend
# 次のコミットへ移行
% git rebase --continue
# 次のeditコミット
% git commit --amend
# 完了
% git rebase --continue

一度に複数追加したファイルを個別のコミットに分けて追加し直したい

これもrebaseにて対象コミットをpickからeditに変更して保存し、Tigを閉じるまでは同じです。コミットし直すためgit reset HEAD^にてstageから纏めて下ろします。

% git reset HEAD^
Unstaged changes after reset:
M XXXXX
M XXXXX

その後、個別にコミットしていきます。コミットが終わったらgit rebase --continueを実行します。stageから下ろしたファイルの中にコミット漏れがあった場合、その後のコミットと衝突する場合があるので注意しましょう。

やり直したい場合は以下のコマンドにてrebase操作をなかったことにできます。

% git rebase --abort

コミットの順番を入れ替えたい

rebase操作時に、コミットの順番を書き換えて保存します。

Before:

pick xxxxxxxx fix workflow
pick yyyyyyyy ワークフロー共通化に伴う調整
pick zzzzzzzz bootstrapを入れないとアカウントが切り替わらない

After:

pick zzzzzzzz bootstrapを入れないとアカウントが切り替わらない
pick xxxxxxxx fix workflow
pick yyyyyyyy ワークフロー共通化に伴う調整

新規ファイルの追加順調整に用いる程度にしておいた方が無難です。

あとがき

コミットは作業ブランチのユニットテストでの修正やGitHub Actions等でのCICD動作検証で細かく多量に積みがちです。一通り動作し終えたら、細かく積んだコミットを見直して一つの塊にし、修正の意図が読み取れやすくしておきましょう。

ただし、書き換えた後に差分に整合性がとれなくなるケースや、他の作業ブランチのマージコミット等のハッシュが弄れないコミットが含まれる場合等、解決が困難になるケースでの利用は避けましょう。

参考リンク