特別な理由なしにgit-flowを新規採用するべきではない

私がこれまでGitの研修講師やブランチ戦略のコンサルティングをおこなってきた経験に基づいて、この記事を書きます。

:::note info
追記:その1: おかげさまで予想以上の反響をいただき、驚いております。少し過激なタイトルになっていましたので、修正いたしました。
:::

:::note info
追記その2: kumagi さんがいいことを書いてくれていた。本当にそうで、git-flow有識者による反論記事待ってます。
:::

https://twitter.com/kumagi/status/1546509861709844481?t=umLSNciRxbHxG7xL2pl9_Q

:::note info
追記その3: 本記事における git-flow とはワークフローのことで、同名のツールのことではありません。
:::


Gitのワークフローについては自転車置き場の議論になりがちであまり乗り気がしないのですが、最近少し発見があったのと、実際に多くの現場で明らかにフィットしないのに git-flow を検討したり採用したりしようとして苦労をしている様を目撃することが多いので書くことにしました。

この記事で主張する内容はタイトルの通りですが、まず前提として以下を宣言しておきます:

* 全てのケースに100%フィットするようなワークフローは存在しない
* git-flowがフィットするケースも探せばあるかもしれない
    * 例えばすでに何年もgit-flowでうまく回せてるよ、など
* どのようなワークフローを採用するかは最終的にはあなた（のチーム）が判断すべき

さて、 git-flow は 2010年1月[「A successful Git branching model」というブログポスト](https://nvie.com/posts/a-successful-git-branching-model/)によってバズり、以来多くの人が参考にしてきたワークフローです。

私個人としては、その重厚なやり方が肌に合わなかったので採用したことはありませんが、美しい図とともに紹介された git-flow は当時Gitが登場してまだ数年、ワークフローについては多くの人が手探り状態だった当時においてはたくさんの人を惹きつけ、以来多数のプロジェクトで採用されてきたようです。

2022年現在においても、新規のプロジェクトや特にルール不明瞭のまま運用してきたリポジトリについて「ワークフローを整理しよう」となったときにまず挙げられるのが git-flow なのではないでしょうか。私が目撃したプロジェクトではだいたい次のようなワークフローが多かったです。

* GitHub Flow風。多数派。ただし２種類あり……
    * 実は明確なルールなしのゆるふわ運用（ほとんどこれ）
    * ガチ勢（ごく僅か）
* git-flow風（上記以外）

特に、ゆるふわ運用から「そろそろちゃんとやるか」と決意して運用方法を選定するときにgit-flowを検討・採用することが多いように思います。

しかし、 git-flow は今時のWebアプリ開発において採用するにはあまりにも重厚すぎて、すぐに改造され簡略化された git-flow「風」の別物のワークフローとして運用されるケースを多く目撃しました。また、git-flowが想定しているような比較的大きな変更を伴うリリースをする開発スタイルは、利用中のリリース＝デプロイされたバージョン1つのみで、細かい変更を高頻度にリリースするWebアプリの開発スタイルとは乖離が大きくなってきています。
つまり、2022年現在ではほとんど誰も git-flow を必要としていないのです。「git-flowをベースにカスタマイズすればよい」のかもしれませんが、後述するとおり git-flow 自体、Gitの特性をあまり考慮せずに作られたワークフローのように思われるので、参考にすべきではないと思います。

英語圏の記事では、実際に git-flow を批判する内容の文書がいくつか見つかります。
（邦訳はあるものの、日本発の文書は見当たりませんでした）

* [Please stop recommending Git Flow!](https://georgestocker.com/2020/03/04/please-stop-recommending-git-flow/) ([日本語訳](https://itnews.org/news_contents/please-stop-recommending-git-flow))
* [Git Flow Considered Harmful](https://blog.ndarwincorn.me/writing/git-flow-considered-harmful/)
* [Is Git Flow Overrated?](https://medium.com/@marioserano55/is-git-flow-overrated-9e4b29a17b8b)
* [Git Flow Is A Bad Idea](https://www.youtube.com/watch?v=_w6TwnLCFwA) （Youtube動画）

以下は批判的な記事というわけではありませんが、冒頭に興味深い注釈があります
* [Gitflow Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) ([日本語訳](https://www.atlassian.com/ja/git/tutorials/comparing-workflows/gitflow-workflow))

上記日本語訳の記事冒頭より引用

> Gitflow とは、元来は Git ブランチを管理するための破壊的で斬新な戦略のレガシー Git ワークフローです。Gitflow の需要は落ち込み、トランク ベースのワークフローが利用されるようになっています。現在ではこれが最新の継続的なソフトウェア開発のベスト プラクティスおよび DevOps プラクティスと見なされています。Gitflow はまた、CI/CD と使用することも困難な場合があります。この投稿では、歴史的な目的で Gitflow の詳細をご説明します。



## git-flow 提唱者の主張

このように英語圏では向かい風が強くなりつつある git-flow ですが、git-flow の本家とも言える、この[ワークフローを世に提案したブログポスト](https://nvie.com/posts/a-successful-git-branching-model/) の現状がどうなっているのか気になって確認したところ、2020年に「Notes of reflection」（反省の注釈）が追記されていたことがわかりました。

その内容をかいつまんで説明すると

* git-flow は10年以上前に考案されたものだ
* 現在のソフトウェア開発はWebアプリを対象とすることが多い
* Webアプリでは
    * 複数のバージョンをメンテする必要はない
    * CICDを採用する
* 10年前当時はこのような開発は一般的ではなかった
* 現在Webアプリを開発していてCICDを採用しているなら GitHub Flow のようなシンプルなワークフローを推奨する
* もしそうでない、複数のバージョンをサポートするようなソフトウェアを開発しているなら git-flow がフィットするかもしれない
* いずれにしろ万能薬はない。あなたが状況を考慮し、採否を決断するべきだ。

私としてはいくつか同意できない点もありますが、 特に Web アプリ開発において git-flow を採用すべきでない、もっとシンプルなワークフローを採用すべき、という点については同意します。

もし2022年現在、Webアプリ開発のワークフローを新たに検討するのであれば、 git-flow は検討対象から除外するのが良いでしょう。
すでに git-flow を採用済みであれば、 GitHub Flow などへの移行を検討しても良いと思います。

## git-flow の要改善点

CICDを前提としたWebアプリ開発に対して git-flow がフィットしない、という点は本家も認めているとおり、あまり異論のないところかと思います。

では、CICDを用いず、複数バージョンをメンテするようなソフトウェア開発では git-flow は依然として有効なのでしょうか？
私個人の見解では、それはNoです。

理由を以下に列挙しますが、ワークフローのディテールに立ち入った話であり、あまり需要もないと思うので大雑把な記述になっています。了承ください。気が向いたら丁寧な説明を書くかもしれません。

:::note info
以下の主張のいくつかは『git-flow に対する批判として適切ではないのではないか』との指摘をいただいています。詳細はコメント欄の[こちら](#comment-86844727e898745c473d)から始まる @skxeve さんと私の議論をご覧ください
:::

:::note info
以下の主張は「複数バージョンをメンテするようなソフトウェア開発」というユースケースを前提としています。したがって単に git-flow に元々存在している改善点だけでなく、複数バージョンをメンテする観点での改善点も含んでいます。
:::

:::note info
以下の主張は GitHub Flow と比較して抽出したものではありません。単に自然なGitの使い方からするとおかしいな、というものを挙げています。どうしても比較対象が必要であれば、後述の[「結局どうすればいいんだってばよ」](#結局どうすればいいんだってばよ)の節で紹介しているワークフローと比較してみてください。
:::

<details><summary>git-flow は誤ったブランチの概念に基づいて設計されている</summary><div>

* 例の図を見るとわかるが git-flow は Subversion のような「あるコミットが永続的に所属するブランチ」を仮定している。しかしながら、このようなブランチの概念はGitにおいては大きな誤解である。
    * 結果として複雑さを不必要に増している
* 以下に記述する多くの問題点が、多かれ少なかれこの誤解に起因しているように見える。
    * Git本来のブランチやコミット、マージ（Fast-Forwardマージを含む）を正しく理解した上でワークフローを設計すれば全く別物になるはず
* もちろんGitにおいて First Parent の系列をSubversion風のブランチとみなすことも可能ではあるし(cf. git-log(1)の --first-parent オプション)、それはそれで大変便利な機能だが、git-flowにおける誤ったブランチの概念をサポートするものではない。
</div></details>

<details><summary>存在意義のないmasterブランチ</summary><div>

* master がリリースタグを追従するだけのブランチにも関わらず、無駄に独立維持している
* この用途なら必要な場所に Fast-Forward マージ（FF）してタグを打てば良いだけ
* hotfix が必要になったときに初めてリリースタグから master 相当のメンテリリース用ブランチを分岐すれば良い
</div></details>

<details><summary>無駄なマージ</summary><div>

* hotfix を個別に develop と master へマージしている
    * そんなに悪いことではない。が:
        * hotfix1個1個について無駄な作業を生んでいる。
* 本来は hotfix を直接マージするのは master だけでよい。
    * develop への修正取り込みは、都合の良いタイミングで master を develop へマージすれば良いだけ。
    * master を定期的に develop へ取り込んでおけば、 develop から(派生したreleaseで)リリースを切りたいときは master をそのコミットへ FF すれば良いだけになる。
        * 前述のとおりmasterはそもそも役に立っていないが
</div></details>

<details><summary>無駄なマージ その2</summary><div>

* release を master へマージしている
    * 無駄なコンフリクト解消が必要になる可能性がある
* 本来は release ブランチで修正が落ち着いたら master を release の先端へ FF すればよい。
    * もちろんこれには前述のとおり develop へ master を都度マージしておかなければ FF できないが。
    * 繰り返しになるが、そもそもgit-flowではmasterは本来不要
</div></details>

<details><summary>異なるバージョンの製品ラインを維持する手順が明示されていない</summary><div>

* 「複数のバージョンをメンテする必要があるなら git-flow はよくフィットする」とは言われているが、実際には異なるバージョンの製品ラインを維持する際の並行管理をどうすれば良いか明示されていない(開発中のdevelopではなくリリース済みのバージョンを複数メンテするときにどうするのかという話です)
    * git-flowの解説では、 hotfix をmaster にマージすることで過去のリリースの修正版をリリースする方法を示している
        * しかし、developをmasterにマージした時点で、過去のリリースに対する修正版をmasterからリリースできなくなるが、その後どのようなブランチで過去のリリースの修正版をメンテしていくかは示せていない
        * つまり、実質的に1つのバージョンを管理する手法しか記載されていない
* 本来はメジャーリリースしたタグ(たとえば v1.0.0, v1.1.0 など)それぞれから独立したメンテ版リリース用のブランチ release_X (release_1_0_x, release_1_1_xなど)を分岐させて、修正コミットを取り込んでいく必要がある。
    * それぞれのメンテ版リリース用のhotfixはそれぞれのメンテ版リリースブランチから分岐する
    * メンテ版リリースは折を見てより新しいメンテ版リリース用ブランチや develop へマージする

(2022/07/19追記) 
git-flow LTS という手法がある、との指摘をコメントでいただき、調査してみました。調査して分かったのですが「そもそも合意のとれた git-flow LTS の定義というものがなさそうだ」という点については指摘しておくべきでしょう。採用しようとしたユーザは「これは本当に git-flow LTS なのだろうか」「うまくいかないが自分の git-flow LTS のやり方が良くないのでは」などと不安になる可能性があります。

[git-flow LTS に関してそれなりに詳しく説明しているWebサイトの記述](https://gitversion.net/docs/learn/branching-strategies/gitflow/examples#support-branches)をひとつの参考として確認したところ、複数バージョンをメンテするうえでのポイントを抑えたルールにはなっているものの、やはり複雑さを無用に足している傾向がありました。逆に言えば既存の git-flow の方向性を継承しているので、git-flow の既存ユーザが複数の製品ラインをサポートする場合には、とっつきやすくて良いと思います。しかし、 git-flow 未採用のところから git-flow LTS を目指すのはお勧めしません。

詳細は[コメント欄の私の投稿](#comment-aae1e5e05f7e82606759)をご覧ください。

</div></details>

<details><summary>黙殺されたrebase</summary><div>
      
* 意図的なのかわからないが git-flow には rebase が全く登場しない
    * ワークフローに登場しないので、git-flow だけをずっと使っているユーザは rebase したいとすら思わないだろう
* 本来、まだマージされていないトピックを develop へマージしようとしたときコンフリクトするなら、トピックを rebase するのが最も簡単な解決方法である
    * にも関わらず git-flow ではどうすれば良いのか示されておらず、rebaseも紹介されていない
    * 偶然だとは思うが、例の美しい図において feature に develop をマージしているように見える箇所がある
* 結果として、コンフリクト解消方法にはrebase以外のさまざまな方法がとられる
* 現場でのワークアラウンドとして「develop を定期的に feature へマージする」という運用を見たことがある（ﾋｪ...）
</div></details>

<details><summary>使い捨て統合ブランチを使っていない</summary><div>

* feature をマージする前にマージして確認する統合ブランチがないので、developがrevertの嵐になりやすい
* 本来はreset可能な使い捨て統合ブランチでテストを含む様子見をしたうえでreset不可の develop 等へマージすべき
    * 特に次期リリースに含めるかどうかまだ未確定の feature など、使い捨て統合ブランチがあればテストや他ブランチとの相互作用を確認可能だが、使い捨てブランチがなければ develop へのマージは「賭け」になる
    * フィーチャーフラグなどで多少は緩和可能ではあるが、使い捨て統合ブランチを用いればコード化不要で git ブランチ運用だけでほとんど同じ効果を得られる
* git-flowはリリース前に release ブランチを用いるが、使い捨て統合ブランチを用いると次のような運用が可能になり、 結果として release ブランチは不要になる
    * リリース前には develop へマージするのは修正用ブランチ/コミットのみとし、 feature は使い捨て統合ブランチのみにマージできるものとする
    * リリース時には master をリリース作業で安定した develop 先頭へFFマージし、タグを打つ。
    * リリース後は必要に応じて安定した feature を develop へマージ可能とする。

:::note info
使い捨て統合ブランチについての詳細は [Git 本家のマニュアル](https://git-scm.com/docs/gitworkflows#_throw_away_integration) または以前私が作成した [プレゼン資料](https://docs.google.com/presentation/d/1IOBGmIaa7pwYngWtg7_TjsRTGsUVn-YEyv03mtiC-3U/present#slide=id.p) をご覧ください。
:::
</div></details>

<details><summary>特殊なメインブランチ名</summary><div>

* メインブランチの名前が master (main) ではなく develop
    * featureの分岐元であり、マージ先であるブランチには普通 master (最近はmainも増えてきている)という名前を用いる
    * git-flow では develop という名前のブランチがその役割を担っており、独特である。（認知負荷や運用負荷が高い）
    * 本来であれば次のような名づけにするべきである
        * git-flow における develop: master または main という名前にすべき
        * git-flow における master: release_x_y などの名前にすべき

:::note info
ここでいう「メインブランチ」とは、「あるリポジトリにおいて、機能追加や修正が最も活発に行われてきた/行われているブランチ」のことです。
:::

</div></details>

### 結局どうすればいいんだってばよ？

前節はなんだかとても複雑なことを言っているように見えますが、 そもそもの git-flow が複雑だからそれを修正しようとすると複雑になるだけで、必要なルールは以下の通りです。(わかりやすさのために名称は git-flow オリジナルに合わせています）

* リリースは適宜 develop を安定させたあとタグを打つことで行う(masterは使わない)
    * リリース版にhotfixが必要になっときに初めてタグからメンテブランチを分岐する
* hotfixはそれを必要とする最も古いメンテブランチから分岐し、そこへマージする
    * その他の統合ブランチは一つ前の世代のメンテブランチをマージすることで間接的に修正を取り込む
* トピック(feature/hotfix)がdevelopにマージするときコンフリクトするならトピック側を rebase する
    * 原則として develop を topic へマージするのは禁止
* featureはdevelopから分岐する
    * nextからは分岐しない
* （必要なら）使い捨て統合ブランチを活用する（ここでは next という名前を使うことにします）
    * featureはいきなりdevelopへマージせず、next へマージしてテストする
    * リリース安定化作業はfeatureのマージを禁止したdevelop上で行う
        * 作業中であってもfeatureをnextへマージしてよい
    * nextがrevertやrebaseされたトピックの再マージ等で複雑になってきたら、一旦 develop へreset
        * 必要なトピックだけマージしなおして綺麗なnextを再構築する

このようなルールであれば release および master という統合ブランチは廃止され develop 一本だけを気にすれば良いことになります。
使い捨て統合ブランチは新規に導入されますが必須ではないですし、これはいつでも develop に reset 可能なので、よくわからなくなったら develop に reset で良いです。masterやreleaseのように慎重に扱う必要はありません。CI環境やローカルのみに使い捨て統合ブランチを置いておく運用もアリです。


:::note info
(再掲) 使い捨て統合ブランチについての詳細は [Git 本家のマニュアル](https://git-scm.com/docs/gitworkflows#_throw_away_integration) または以前私が作成した [プレゼン資料](https://docs.google.com/presentation/d/1IOBGmIaa7pwYngWtg7_TjsRTGsUVn-YEyv03mtiC-3U/present#slide=id.p) をご覧ください。
:::


## まとめ

* 2022年現在のWebアプリ開発において git-flow はフィットしない可能性が高い (git-flow提唱者自身がそれを認めている)
* git-flowには他にも色々と要改善点があるので、Webアプリでない場合も採用するときは良く考えてから採用すべき。

以上です。