普段思ったことや、雑記。

Menu & Search

Gitのマージ時にsquashによるコミットを使用することは本当にダメなのか

2020年10月11日

某社で『マージ時のsquashによるコミットはありえない』、と技術顧問の方が言っているらしく…

上記の言葉が一人歩きして『いかなる場合も』と伝わってしまったのかもしれないが。チームメイトからマージコミットなしで、squashによるコミットはダメという意見が多数だったので、特に明確な根拠がなかったので、自分で再考してみることにした。なお、作業ブランチにおいて、適切な粒度でsquashすることがダメというのではなくて、あくまでマージ時にまとめてsquash、ということにのみ言及している点に注意されたい。

マージ時にブランチのsquashコミットを使用するケースの特徴

GitHubやGitLabのマージ実行時のオプションにより、マージ時にsquashして1つのコミットにしてしまうと、大きな特徴として、通常のマージと異なり、マージコミットが発生しない。その代わり、マージ対象のコミットがまとまって1つのみのコミットとなる。この時点で、マージコミットを検知して何かしているソフトウェアを使用している場合には、支障が出る。

コミットメッセージは、参考までに、GitHubでのsquash and mergeオプションによるプルリクエストのマージでは、プルリクエスト名とプルリクエストの番号が記載される。GUIなどでブランチを見る場合を想定すると、マージコミットがある場合、作業ブランチからマージ対象ブランチへ合流する形で表示がなされる。一方で、squashを使用してマージをすると、作業ブランチからマージ対象ブランチへ合流するようには表示されない。突然1つのコミットがマージ対象ブランチにコミットされるのみである。あとは、マージ後、revertする場合にマージコミットがある場合とは少し操作が異なってくる(※当然コミットの属性が通常コミットとマージコミットで異なるので)。

マージコミットとは

そのままその通り、マージ時に発生するコミットログである。通常コミットと異なるのは、マージ実施時に、どのブランチから、どのブランチへ、マージしたという情報が記載されていることだ(Merge: コミット番号 コミット番号)。それ以外で大きく通常コミットと特徴が異なるか、と言われると他はマージの旨など、コミットメッセージが記載されているのみで異ならない。

マージ時のsquashによるコミットはいかなる場合にも不可であるのか?

前述の通り、当然コミットをまとめたものはGitの歴史上、新規コミットでしかないため、sqaushコミットでマージする場合にはやはり色々と問題とされそうなことが出てくる。しかし、実際のユースケースを考えた場合にまた少し変わってくると思う。あくまで私個人の意見だが、Gitはドキュメントの変更履歴をストックする機能に長けたソフトウェアだと思うので、その利点を活用するためであるのなら、潔癖症にならない程度に使うのはアリなんじゃないかと思う。私のスタンスとして、当然前述のようなマージコミットとの挙動の際も理解して使っており、プロジェクトの状況によりsqushコミットをマージするのか、それを不可とするのかを使い分けている。以降では、厳密になりすぎないGitの運用で、squashを使ってもよさそうなシチュエーションを紹介する(※当然、前提として、squashしてマージしても良い、という状況のもとで)。

1. 自分以外のコミットが作業ブランチに入っていない

自分以外の第三者のコミットが入っている場合、マージ時にsquashしてしまうと他の方のコミットも当然1つのコミットとなってしまう。事前に同意やルールがあれば良いが、これはあまり良くなさそうなので、基本的にはやらない。逆に自分のコミットのみの場合、履歴として厳密に管理すべきか・否かの判断でsquashするのであるから、使用には問題ないだろう。

2. リリース、開発ブランチなど、基幹となるブランチの手前のブランチにマージする場合

いわゆるmasterやdevelopなど、チーム開発で基幹となるブランチにはマージコミットを発生させたほうが良さそうではある、というのは大いに理解しているところである。しかし、そうでない場合、例えば開発対象の機能Aがあったとして、そのための「一時的な機能Aのための開発ブランチ」に対するマージは状況により例外と考えている。ここでも、1のように、自分以外の第三者のコミットがある場合は控えたほうが良いかもしれない。自分のみで機能Aを開発している場合、機能A用の開発ブランチは、ある程度ラフにマージし、機能Aのコミットがある粒度でまとまった状況で、最終的にリリースブランチや開発ブランチにマージされればよいと考えている。

では、マージ時squashを禁止するとどうなるのか?

私はコミット粒度が小さく、コミットコメントが雑である(コミットメッセージには、fixとかfとか書いている。無論、プルリクエストには変更の内容を記述している)。もはや、squashなしではGitを扱えない人種なのだ。ここでは、マージ時にsquashを禁止されるとどうなるか、紹介したい。

手元でsquashし、ブランチにforce pushする

当然、過去のコミットをまとめ、新しいコミットを作ると、それがリモートリポジトリにすでにアップロードされている場合、リモートリポジトリの歴史が変わってしまう。シチュエーションとして、コミットをまとめたくなる場合、うまくやれる人はリモートリポジトリへのアップロード前であるとか、プルリクエストの前にあるいくつかのコミットにまとめるが、そもそも私のようにコミットが雑な人間はそのようなことができない。
そうなると、ソースコードはすでにリモートリポジトリに存在し、プルリクエストのソースコードのレビュー後に、いつもコミットをまとめ始めるということになる。コードレビュー後にあるリモートリポジトリのコミットをまとめると、当然歴史が変更される。
この場合、強制的にこれまの変更履歴の歴史を書き換える必要があり、それを手元で行うことになる。マージコミットを発生させる、というルールに則るがために、わりかしリスキーな作業を手元で行っているのだ。これをやってしまうと、リスク以外にも労力がかかる。おそらく厳密にやると、squashしたコミットはまぎれもない『新しいコミット』であるし、以前の歴史改変前から変更漏れがないか、とsquash後のコードレビューも必要となる。
正直、こうあらためて書いてみると、果たしてそこまでしてマージコミットが欲しいのか、とも思える。なお、force push禁止の状況もあると思うので、その場合は当然新規ブランチにプッシュする。マージコミットが欲しいがためにクローンのブランチが誕生した。素晴らしい!

squash時にマージコミットはダメなのか、はケースバイケース。禁止してしまうことで、人為的な変更のミスや、本来不要なブランチが発生することが考えられる

完全にルールで縛ってしまい、マージ時にsquashをすると、人間が介入する操作が増えてしまって、リスクや労力となる。これを、マージコミット…冒頭に書いた通り、どのブランチからどのブランチへマージした、と記載があるだけのコミットが欲しいがために完全にNGとしてしまう意見には、私は否定的である。
では、現代的なプロジェクトで、マージ時にsquashをした場合にどこのブランチからどこのブランチへマージした、という情報は取得ができないのか、というとそうでもない。(※環境によるが)例えば、GitHubを使用しているのなら、squash and mergeオプションによるマージ実行のコミットを、GitHubの履歴で確認すると、プルリクエストが紐づいているため、どのブランチからどのブランチへマージされた、というのは追うことができる。
Gitの純粋なコミットログにおいても、マージ属性のコミットはあったほうがより良いのは理解しつつ、現代的なソフトウェアのフォローにより、マージコミットが無かったとしても、マージ時の情報の補完は十分に可能となっている。

Article Tags
mmiyauchi

プログラムを書きながらTranceを聴くのが良いですね。みなさんも聴いたほうがいいですよ、Trance。EDMよりハードトランスでしょ。

Related article

クソコードとは何であるか

そういえば、クソなコ…

Reactの高度な状態管理にRedux+Sagaが定番となっていることについての疑問

久しぶりにReact…

Pythonが好きではないいくつかの理由

Pythonコードを…

Discussion about this post

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください

Type your search keyword, and press enter to search
%d人のブロガーが「いいね」をつけました。