TT Lab
はじめる
学ぶ 学習パス コース

Git実戦

戦略を選ぶ問いは一つだ

TT Labで続きを見る

一言でいうと

ブランチ戦略を選ぶときの問いは、「どの図がきれいか」ではなく、「障害が起きたとき、このリポジトリで原因のコミットをどう見つける計画か」です。

なぜ必要なのか

Git Flow、GitHub Flow、Trunk-Basedは、それぞれ異なるデプロイ周期を前提に作られています。前提を無視して名前だけを持ってくると、毎日摩擦が生じます。

戦略 ブランチの種類 マージの頻度 向いている場面
Git Flow 5種類以上 週1–2回 定期リリース、モバイルアプリ、パッケージ
GitHub Flow 2種類 日1–3回 CDを実践するSaaS
Trunk-Based 1–2種類 日5–10回以上 ブランチの寿命が1日以内、Feature Flagが必須

Trunk-BasedはCIへの要求水準が非常に高いです。Feature Flagなしで始めると、未完成のコードがそのままデプロイされます。逆に、GitOps環境では、Git Flowのreleaseブランチが自動同期のパターンと衝突しやすくなります。そのため、GitOpsを使うなら、GitHub FlowまたはTrunk-Basedを勧めます。

共通のルールが1つあり、どこでも通用します。3日以上生きているfeatureブランチは、マージコンフリクトの温床です。

どう動くのか

マージ方式は3つあり、それぞれ残るものが違います。

チェリーピックは、保存されたdiffを切り貼りするものではありません。Gitはそもそもdiffを保存せず、スナップショットだけを保存します。そのため、対象コミットの親のツリーを共通祖先として、3-wayマージを行います。チェリーピックが衝突する理由が、ここにあります。

現場での姿

ホットフィックスをリリースブランチで修正して、mainに反映せず、次のリリースで同じバグが復活することは、どのチームでも一度は起きます。リリースブランチを運用するなら、「ホットフィックスは必ずmainにも取り込む」が、ポリシーの文として書かれている必要があります。

タグも同じです。軽量タグは単なるポインターなので、誰がいつなぜ付けたのかが残りません。リリースタグには-aで注釈を付けておくと、あとで調査に使えます。

履歴は調査のために残す

ブランチ戦略の価値は、平常時ではなく、事故が起きたときに表れます。「昨日まで動いていたのに、今日は動かない」という状況で、原因のコミットを探す標準的なツールは二分探索です。Gitはgit bisectで、これを半自動で行ってくれます。正常なコミットと壊れたコミットを教えると、中間地点をチェックアウトしてくれ、人は良い/悪いだけを答えれば、範囲が半分ずつ狭まります。コミット1,000個の中から原因を見つけるのに、10回で十分です。

二分探索がうまくいくには、履歴が2つの条件を満たす必要があります。コミット1つ1つがビルドできて動作する必要があり、コミットのサイズが小さい必要があります。途中に動作しないコミットが混じっていると、人が良い/悪いを答えられず、その地点を飛ばすことになりますし、スカッシュマージでPR1つがコミット1つになっていると、探索がそのPRの手前で止まります。「このPRのどこか」までしかわからず、残りは手で読む必要があるという意味です。前に、スカッシュマージが二分探索の解像度をPRのサイズに固定すると述べましたが、それはこの部分のことです。

原因のコミットを見つけたあとの選択も、あらかじめ決めておく必要があります。元に戻す方法は2つあり、性質がまったく違います。

マージコミットを元に戻すときは、もう1つ必要なことがあります。親が2つあるため、どちらの系統を維持するかを-m 1のように指定する必要があり、通常、1番目の親がマージを受けた側(main)です。そして、マージを元に戻したあと、同じブランチをもう一度マージしても、何の変更も入りません。Gitから見ると、すでにマージされたコミットだからです。元に戻したものを再び入れるには、取り消しのコミットをもう一度元に戻す必要があるという事実を、急いでいる状況で初めて学ばないほうがよいです。

次のラボですること

リポジトリを最初から作り、機能ブランチを--no-ffでマージして統合の記録を残し、小さな修正は早送りで取り込み、リリースタグを付け、リリースブランチのホットフィックスをチェリーピックでmainに反映したあと、マージ済みのブランチだけを選んで整理します。最後に、このリポジトリの実際の数字を数えて、戦略の比較表を書きます。