シンプルで事故が起き辛いGitのブランチフローを考えて実務で運用した話

これは、かつて参画した GitHub を利用したプロジェクトでブランチフローが悪く事故が多発したので考案し、運用した内容です。
基本的には後々の運用を考えた時に情報源になり、かつ Git 操作に極力手間を取られることがなく、CI/CD を回しながら品質を維持できる内容で考えています。

フローの要件として考えたこと

取り回しが単純明快であること

開発以外の要素に振り回されないように単純なフローにしていて、世に言われる履歴の綺麗さとか言うのは個人的には関心が薄いので重視していません。その代わりコミットが壊れないことや、インデックスとして見やすくなるような部分を重視しています。

GitHub との相性が良いこと

まず個々の開発タスクを Issue ベースで管理し、Pull Request ベースで取り込む運用としました。
Pull Request の取り込み方式は Squash Merge とし、メインブランチのコミット履歴が Pull Request のマージコミットになるようにしました。
これはメインブランチのコミット履歴は Pull Request のマージコミットだけあれば後から追えるというのと、メインブランチのコミットログを単純にする意味でこの方式にしています。

CI/CD を活用しやすいこと

これは割とどこでもやっていると思いますが、ブランチ名に develop とか staging, production とか付けて管理することで、自動的に環境を識別できるようにしました。

実際に運用したフロー

フロー図

フローの運用内容

  • develop/main ブランチを最新ブランチとする運用
    • develop/main ブランチ相当のものが複数ある状態というのが世の中にあると思いますが、管理が非常に大変なのでそれはしない方向にしました
  • 機能ブランチを develop/main ブランチに取り込むのは Squash Merge
    • 基本的に変更履歴を見る時は GitLens や blame で変更行から Pull Request を当てて、そこを見に行くという運用にしていました
  • 機能ブランチに develop/main ブランチを取り込むのは merge
    • 一般的には rebase が多いと思いますが、次の観点から採用しませんでした
      • どのポイントで取り込んだのかわからない
      • merge と比較した場合にコンフリクト対応に手を取られる
        • 経験上ここで事故が頻発する
      • 素直に push 出来ない
    • merge であれば以下のように単純な流れに出来ますし、push 前に差分確認して事故を防ぐことも容易です
      • git switch develop/main
      • git pull
      • git switch -
      • git merge -
      • コンフリクトがあれば解消
      • git push
  • デプロイ方式によるルートブランチ分割
    • ルートブランチ名によってデプロイ先を変更できるようにしました
    • GitHub Actions でブランチ名を拾って環境変数を差し替えることでデプロイするワークフローを組んでいます
    • staging/production/ ブランチは develop/main から checkout する運用です
      • これらはデプロイするためだけの使い捨てブランチなので毎回生えます

あとがき

このフローの利点としてはマージでコンフリクトが起きても基本的に Current と Incoming を一回比較するだけで済むのでコンフリクトの解消が簡単で、コンフリクト時は Incoming が壊れないように Current を直すのが基本になり、Current を優先する場合は適宜上書きするといった内容です。

ブランチの合流は merge だとマージコミット分一回の解消だけでいいので事故の発生要因が低いのが rebase と比較した時の利点です。rebase だとコミット回数分再帰的に合流させる必要があるのでブランチの寿命が長かったりすると苦行になってきます。

このフローができた経緯としては元々は GitHub Flow をベースにしていたのですが、色々やっていくうちにこうしたらもっと良くなるのではないか?というのを試行錯誤していてこの形に落ち着いたのですが、後から調べたら GitLab Flow に近い形式に見えたので、似た内容は既に誰かが考えているものだなと感じました。

上で挙げた内容の他にも GitHub のリポジトリ設定でブランチ保護のルールを設定したり、Pull Request のマージ設定で Squash merging だけ許可したり、ヘッドブランチの自動削除をするなど、基本的に面倒なことを考えたり、しなくて良い様にするなど、開発に注力しやすいように環境を整えると心理的な抵抗が少なく運用できて便利だなと感じています。