カルキチブログ

Git flowでリリースを行うときのマージ戦略について

リリース作業を行うときにGitで盛大にやらかしたので、戒めとして書き残すことにしました。

事のおこり

git flowの流れに沿ってリリースを行っていたのですが、2回目のリリース作業時、リリースを行うときに何故かリリースブランチからmainにマージを行うときに大量のコンフリクトが発生していました。

最初のリリースを行ったときにgitflowの公式に則って、マージを行ったのに何故コンフリクトが発生しているのか全くわかりませんでしたが、同じチームの人に相談したところ、一つとんでもないミスを犯していたことが発覚しました。

何をやらかしたのか

結論としては、管理画面上でマージを行うときのマージ戦略の選択を誤っていました。

GitHub(Bitbucketとかもそうですが)では管理画面上でPRを作ってマージをしようとすると以下の3つのマージ戦略を選択することができます。

  • Create a merge commit
  • Rebase and merge
  • Squash and merge

マージ戦略に関してはこちらの記事が非常に参考になります。

https://qiita.com/ko-he-8/items/94e872f2154829c868df

今回僕は、以下のような形でマージを行なってしまっていました。

release→main・・・Squash and merge

release→develop・・・Squash and merge

releaseからdevelopにマージをするときは問題ありませんが、releaseからmainに対してSquash and mergeでマージを行うと深刻な問題が発生しています。

どのような問題かというと、リリースブランチをマージした後にdevelopに変更を加えて、リリース作業を行うとほぼ確実にコンフリクトが発生してしまいます。

何故このような現象が発生するかですが、Squash and mergeでマージを行うと以下のような状態でマージが行われます。

  • マージコミット・・・あり
  • 元のコミットログ・・・残らない
  • 元ブランチとの関係・・・残らない

元のコミットログが残らないのが大きな問題で、変更内容はSquash and mergeでもmainに取り込むことができますが、マージ元のコミットログを取り込んでいないので、いつどのコミットで変更を加えたのかがmainブランチ側で認識できなくなります。

その状態で、同じ箇所を編集しようとするといつどのコミットで変更を加えたのかが認識できずコンフリクトが発生すると言うわけです。

どうすればよかったのか

本来だったら、このようにマージを行う必要がありました。

release→main・・・Create a merge commit

release→develop・・・Squash and merge

Create a merge commitは、GitHubのデフォルトのマージ戦略です。

Create a merge commitでマージを行うと、以下のような状態でマージが行われます。

  • マージコミット・・・あり
  • 元のコミットログ・・・残る
  • 元ブランチとの関係・・・残る

元のコミットログを取り込みつつマージできるので、いつどのコミットで変更を加えたのかmainブランチ側で認識できるようになるので、コンフリクトが発生する心配はありません。

Squash and mergeは使用してはいけないものなのか?

個人的にですが、使用してはいけないものではないと考えています。

Squash and mergeを使用せずに、Create a merge commitのみでマージを行うと軽微な修正のコミットも全てGitのログに記録されてしまうので、Gitグラフが煩雑でみづらくなってしまいます。

featureブランチを切って機能実装を行うときは細かい粒度でコミットを切ることがほぼだと思うので、featureからdevelopにマージを行うときはGitグラフを綺麗に保つ意味でもSquash and mergeの方がいいと思います。

再発防止策

一旦は以下のようなルールを設けておけば大きな問題は発生しないのではと言う結論に至りました。

  • release→main・・・Create a merge commit
  • release→develop・・・Squash and merge

まとめ

  • Githubの管理画面上でマージを行う方法は3種類ある
  • Create a merge commit・・・リリースブランチからmainにマージするときに使用する。元のコミットログを取り込みつつマージできる
  • Rebase and merge・・・マージ元のブランチの関係は残らないし、マージコミットも残らないが、rebaseするのでマージ元のコミットログは残る
  • Squash and merge・・・複数のコミットをひとまとめにするときに使用する。マージコミットはできるが、元のコミットログは残らない

おまけ

Rebase and mergeでも元のコミットログは残るから、問題は起きなさそうなのですが、どうなんでしょうか?

Rebase and mergeでマージすると、、、

  • マージコミット・・・なし
  • 元のコミットログ・・・残る
  • 元ブランチとの関係・・・残らない

こういう感じになります。

まあ、一つ確実に言えることはGithubの管理画面上でマージを行うときは気をつけましょうって感じです。

ちなみにですが、今回の記事を執筆するきっかけになったトラブル自体はリリース作業を一度やり直して、develop、mainそれぞれマージし直すことで解決しました。