強化されたGitフローの説明

公開: 2022-03-11

不注意でGitにダメージを与えるのは、とても簡単なことです。 それでも、Gitを使用する最良の方法は常に物議を醸すでしょう。

これは、Git自体が基本的な分岐操作の詳細のみを示しており、その使用パターン、つまり分岐モデルはユーザーの意見の問題であるためです。 Git分岐モデルは、ソフトウェア開発者がコードベースに変更を加えるときに必然的に発生する混乱を整理することで、苦痛を和らげることを約束します。

多くの開発者と同様に、実際のソフトウェア開発に取り掛かることができるように、「正しく機能する」ものが必要でした。 そこで、Gitユーザーによく推奨される分岐モデルであるGitフローを取り上げました。 たぶん、実際にいくつかの障害にぶつかるまで、最初はGitフローのロジックに参加していました。 あるいは、Gitフローはあなたがそれを採用するのに十分に適合していないように思われるかもしれません。 結局のところ、数え切れないほどの変数が働いており、すべての状況で単一の分岐モデルがうまく機能するわけではありません。

朗報です! 従来のGitフローモデルのバリエーションである拡張Gitフローは、主な利点を維持しながら、Gitフローワークフローのより一般的な操作を簡素化します。

クラシックGitフローモデルの素晴らしさと悲惨さ

大幅な値の増分(つまり、リリース)で進化する製品を開発するときに、Gitフローがどのように優れているかを発見して以来、私はGitフローを強く支持してきました。

スクラムベースの開発で通常使用される2週間以上のスプリントのように、値を大幅に増やすには、完了するまでにかなりの時間が必要です。 開発チームがすでに本番環境にデプロイされている場合、次のリリースのスコープが本番環境のコードが存在する同じ場所、たとえば、使用するGitリポジトリのメインブランチに蓄積されると、問題が発生する可能性があります。

製品がまだ初期開発段階にある間、つまり、製品の生産も実際のユーザーもいない間は、チームがすべてをメインブランチ内に保持するだけで問題ありません。 実際、それは大丈夫以上のものです。この戦略により、多くの儀式なしで開発の最速のペースが可能になります。 しかし、実稼働環境では状況が変わります。 その後、実際の人々は安定するために製品に依存し始めます。

たとえば、本番環境に重大なバグがあり、すぐに修正する必要がある場合、修正を展開するためだけに、開発チームがこれまでにメインブランチで発生したすべての作業をロールバックしなければならないことは大きな災害になります。 また、適切なテストを行わずにコードをデプロイすることは、コードが中途半端であると見なされるか、十分に開発されていると見なされるかにかかわらず、明らかにオプションではありません。

ここで、Gitフローを含む分岐モデルが光ります。 洗練された分岐モデルは、現在人々が使用しているシステムのバージョンから次のリリースを分離する方法、次のリリースでそのバージョンを更新する方法、および現在のバージョンに重大なバグの修正プログラムを導入する方法に関する質問に答える必要があります。

Gitフロープロセスは、「メイン」(本番または「現在のバージョン」ブランチ)と「開発」(開発または「次のリリース」ブランチ)を分離し、機能/リリース/ホットフィックスブランチの使用に関するすべてのルールを提供することで、これらの基本的なシナリオに対処します。 これは、リリースベースの製品の開発ワークフローからの多くの頭痛の種を効果的に解決します。

しかし、古典的なGitフローモデルに適したプロジェクトでも、それがもたらす可能性のある典型的な問題に苦しんでいます。

  • Gitフローは複雑で、2つの長寿命のブランチ、3種類の一時的なブランチ、およびブランチが相互に処理する方法に関する厳格なルールがあります。 このような複雑さは間違いを起こしやすくし、それらを修正するために必要な労力を増やします。
  • リリースブランチとホットフィックスブランチでは、「ダブルマージ」が必要です。一度メインに、次に開発に。 時々、あなたは両方をするのを忘れることができます。 スクリプトまたはVCSGUIクライアントプラグインを使用してGitフローの分岐を簡単にすることができますが、特定のプロジェクトに関与するすべての開発者のすべてのマシンに対して、最初にそれらを設定する必要があります。
  • CI / CDワークフローでは、通常、リリースの最終ビルドが2つになります。1つはリリースブランチ自体の最新のコミットからのもので、もう1つはメインへのマージコミットからのものです。 厳密に言えば、メインの1つを使用する必要がありますが、通常2つは同一であるため、混乱する可能性があります。

「EnhancedGitFlow」と入力します

私が初めて拡張Gitフローを使用したのは、グリーンフィールドのクローズドソースプロジェクトでした。 私は他の1人の開発者と協力しており、メインブランチに直接コミットすることでプロジェクトに取り組んでいました。

注:製品の最初の公開リリースまでは、開発ワークフローの速度と単純さのために、Gitフローの支持者であっても、すべての変更をメインブランチに直接コミットすることは絶対に理にかなっています。 まだ本番環境がないため、チームができるだけ早く修正する必要がある本番環境のバグの可能性はありません。 したがって、古典的なGitフローが意味するすべての分岐魔法を実行することは、この段階ではやり過ぎです。

その後、最初のリリースに近づき、その時点を超えると、メインブランチに直接コミットすることに不安を感じることに同意しました。 私たちはかなり急速に動き、ビジネスの優先順位は堅実な開発プロセスを確立する余地をあまり残しませんでした。つまり、メインブランチがリリース準備状態にあることを確信できる十分な自動テストを備えたプロセスです。

これは、古典的なGitフローモデルの有効なケースのようです。 メインブランチと開発ブランチが別々であり、値の増分が大幅に増加するまでの時間が十分にあるため、ほとんどの場合、手動のQAで十分な結果が得られると確信していました。 私がGitフローを提唱したとき、同僚は似たようなものを提案しましたが、いくつかの重要な違いがあります。

最初、私は押し返しました。 古典的なGitフローに提案された「パッチ」のいくつかは少し革新的すぎるように思えました。 私は彼らが主要な考えを破るかもしれないと思いました、そして全体のアプローチは不十分でしょう。 しかし、さらに考えてみると、これらの調整は実際にはGitの流れを壊さないことに気づきました。 一方、上記のすべての問題点を解決することで、Gitの分岐モデルを改善します。

そのプロジェクトで変更されたアプローチで成功した後、私はその背後に小さなチームがいる別のクローズドソースプロジェクトでそれを使用しました。 このプロジェクトでは、6か月で本番環境に移行し、それ以来、CIおよびE2Eテストを1年以上使用しており、毎月リリースされています。

拡張Gitフローを使用する場合の典型的なGitコミットグラフ。グラフは、開発とメインでのいくつかのコミットと、それらの一般的なコミットの前に、いくつかの日付ベースのタグを示しています。

この新しい分岐アプローチに関する私の全体的な経験は非常にポジティブだったので、他の開発者と共有して、従来のGitフローの欠点を回避できるようにしたいと思いました。

従来のGitフローとの類似点:開発の分離

強化されたGitフローでの作業の分離については、メインと開発の2つの長寿命のブランチがまだあります。 (ユーザーはまだ修正プログラムとリリース機能を持っています。これらはもはやブランチではないため、「機能」に重点を置いています。詳細については、相違点のセクションで説明します。)

従来のGitフロー機能ブランチの正式な命名スキームはありません。 機能の準備ができたら、開発から分岐してマージし直して開発します。 チームは、任意の命名規則を使用できます。または、開発者が「my-branch」よりもわかりやすい名前を使用することを単に望んでいます。 強化されたGitフローについても同じことが言えます。

あるカットオフポイントまで開発ブランチに蓄積されたすべての機能が、新しいリリースを形作ります。

スカッシュマージ

ほとんどの場合、履歴を適切かつ線形に保つために、機能ブランチにスカッシュマージを使用することを強くお勧めします。 これがないと、チームがほんの一握りの機能ブランチをジャグリングしているときに、コミットグラフ(GUIツールまたはgit log --graphから)がだらしなく見え始めます。

スカッシュマージ戦略から得られたコミットグラフを、マージコミット戦略から得られたものと比較します。最初のGitコミットグラフには「開発」というラベルの付いたプライマリブランチがあり、以前のコミットには「feature-D」と「feature-C」が分岐しており、さらに以前のコミットには「feature-B」と「feature-A」があります。 「それから分岐します。マージコミットの結果は似ていますが、各機能ブランチにより、開発に結びついた時点で新しいコミットが発生します。スカッシュマージの結果は、他のブランチがなく、単なるコミットの直線として発展しました。

ただし、このシナリオのビジュアルに問題がない場合でも、潰す理由はもう1つあります。 押しつぶさずに、コミット履歴ビュー( --graphなしのプレーンなgit logとGitHubの両方)は、最も単純なマージシナリオでも、かなり一貫性のないストーリーを伝えます。

スカッシュマージ戦術から得られたコミット履歴ビューを、マージコミット戦術から得られたビューと比較します。元のリポジトリの状態はタイムライン形式で示され、開発時の共通のコミット「x」からの2つの機能ブランチへのコミットの時系列を示し、ブランチ間でコミットを交互に行います。つまり、1a、2a、1b、2bの順序です。マージコミットの結果は、2つのバリエーションで表示されます。 2番目のブランチをマージし、最初のブランチをマージしてから両方のブランチを削除した結果のコミット履歴では、ストーリーは時系列ですが、まとまりがなく、最初のブランチの追加のマージコミットが含まれています。ブランチを削除する前に順番にマージした結果の履歴では、コミットは「x、2a、1a、2b、1b」の順序で並べられ、その後に2番目のブランチのマージコミットが続きます。これは時系列ではありません。スカッシュマージからのコミット履歴には、機能ブランチごとに1つのコミットがあり、各ブランチのストーリーはコミッターによって伝えられます。

スカッシュマージを使用する際の注意点は、元の機能のブランチ履歴が失われることです。 ただし、GitHubを使用している場合、この警告は適用されません。GitHubは、機能ブランチ自体が削除された後でも、スカッシュマージされたプルリクエストを介して機能ブランチの元の履歴全体を公開します。

従来のGitフローとの違い:リリースとホットフィックス

(うまくいけば)それがあなたがする主なことなので、リリースサイクルを見てみましょう。 開発で蓄積されたものをリリースしたいところまで来たら、それは厳密にはメインのスーパーセットです。 その後、クラシックと拡張Gitフローの最大の違いが始まります。

強化されたGitフローで通常のリリースを実行すると、Gitコミットグラフが変化します。最初のグラフは、チップの背後にあるいくつかのコミットの開発と1つのコミットによる主な分岐を持っています。タグ付け後、mainとvYYYY-MM-DDは互いに均等になります。ローカルメインを削除し、開発、強制プッシュ、デプロイ、テストなどの先端で作成すると、開発でもメインが表示され、メインが元々あった場所にvYYYY-MM-DDが残ります。デプロイ/テストサイクルの後、メインのステージング修正(最終的にはスカッシュが開発にマージされます)、および開発の無関係な変更、最終的なグラフは開発され、メインが分岐し、それぞれがいくつかのコミットを持ち、前のグラフ。

拡張Gitフローでのリリース

強化されたGitフローを使用してリリースを作成するすべてのステップは、従来のGitフロープロセスとは異なります。

  1. リリースは、開発ではなく、メインに基づいています。 メインブランチの現在の先端に意味のあるタグを付けます。 現在の日付に基づいて、接頭辞「v」が付いたISO 8601形式のタグを採用しました(例: v2020-09-09 )。
    • 修正プログラムなど、1日に複数のリリースが発生した場合は、必要に応じて、フォーマットに連番または文字を付けることができます。
    • 通常、タグはリリース日に対応していないことに注意してください。 これらは、Gitに、次のリリースプロセスが開始されたときにメインブランチがどのように見えたかを参照するように強制するためだけのものです。
  2. git push origin <the new tag name>を使用してタグをプッシュします。
  3. その後、少し驚きます。ローカルのメインブランチを削除します。 間もなく復元するので、心配しないでください。
    • mainへのコミットはすべて安全です。前の手順でmainにタグを付けることで、ガベージコレクションからコミットを保護しました。 これらのコミットのすべて(後で説明する修正プログラムも含む)も開発の一部です。
    • チームの1人だけが特定のリリースでこれを実行していることを確認してください。 それがいわゆる「リリースマネージャー」の役割です。 リリースマネージャーは通常、最も経験豊富な、および/または最も上級のチームメンバーですが、チームは、特定のチームメンバーがこの役割を永続的に引き受けることを避けるのが賢明です。 悪名高いバスファクターを増やすために、チーム間で知識を広める方が理にかなっています。
  4. 開発ブランチのチップコミットで新しいローカルメインブランチを作成します
  5. リモートリポジトリはこのような「大幅な変更」を簡単に受け入れないため、 git push --forceます。 繰り返しますが、これは、このコンテキストで見られるほど安全ではありません。理由は次のとおりです。
    • メインブランチポインタをあるコミットから別のコミットに移動するだけです。
    • 一度に1人の特定のチームメンバーだけがこの変更を行っています。
    • 毎日の開発作業は開発ブランチで行われるため、メインをこのように移動しても、他の人の作業を中断することはありません。
  6. 新しいリリースがあります! ステージング環境にデプロイしてテストします。 (便利なCI / CDパターンについては以下で説明します。)修正はすべてメインブランチに直接送信され、そのために開発ブランチから分岐し始めます。
    • 同時に、開発ブランチで新しいリリースの作業を開始できます。これは、従来のGitフローで見られるのと同じ利点です。
    • この時点で現在本番環境にあるもの(ステージングの次のリリースではない)に修正プログラムが必要な場合は、以下の「アクティブなリリース中の修正プログラムの処理...」にこのシナリオの詳細があります。
  7. 新しいリリースが十分に安定していると見なされたら、最終バージョンを本番環境にデプロイし、mainを1回スカッシュマージして開発し、すべての修正を取得します。

拡張Gitフローのホットフィックス

修正プログラムのケースは2つあります。 アクティブなリリースがないときに修正プログラムを実行している場合、つまり、チームが開発ブランチで新しいリリースを準備している場合は、簡単です。メインにコミットし、変更をデプロイして、準備ができるまでステージングでテストします。本番環境にデプロイします。

最後のステップとして、メインからコミットを選択して開発し、次のリリースにすべての修正が含まれるようにします。 複数のホットフィックスコミットが発生した場合、特にIDEまたは他のGitツールで簡単に実行できる場合は、何度もチェリーピッキングする代わりにパッチを作成して適用することで、労力を節約できます。 最初のリリース後にマージメインを押しつぶして開発しようとすると、開発ブランチで行われた独立した進捗状況と競合する可能性が高いため、お勧めしません。

アクティブなリリース中、つまり、メインを強制的にプッシュして新しいリリースを準備しているときにホットフィックスを処理することは、拡張されたGitフローの最も弱い部分です。 リリースサイクルの長さと解決しなければならない問題の重大度に応じて、常に新しいリリース自体に修正を含めることを目指してください。これが最も簡単な方法であり、ワークフロー全体をまったく混乱させることはありません。

それがうまくいかない場合(修正をすばやく導入する必要があり、新しいリリースの準備が整うのを待つことができない場合)、やや複雑なGit手順の準備をします。

  1. ブランチを作成します—これを「新リリース」と呼びますが、チームはここで任意の命名規則を採用できます—メインの現在のヒントと同じコミットで。 新規リリースをプッシュします。
  2. 現在アクティブなリリース用に以前に作成したタグのコミット時に、ローカルメインブランチを削除して再作成します。 メインを強制的に押します。
  3. mainに必要な修正を導入し、ステージング環境にデプロイして、テストします。 準備ができたら、本番環境にデプロイします。
  4. チェリーピッキングまたはパッチを使用して、現在のメインリリースから新しいリリースへの変更を伝達します。
  5. その後、リリース手順をやり直します。現在のメインの先端にタグを付けてタグをプッシュし、新しいリリースブランチの先端にあるローカルメインを削除して再作成し、メインを強制的にプッシュします。
    1. 前のタグはおそらく必要ないので、削除できます。
    2. 新しいリリースのブランチは冗長になっているため、削除することもできます。
  6. これで、新しいリリースでいつものようにうまくいくはずです。 緊急修正プログラムをメインから伝播して、チェリーピッキングまたはパッチを介して開発することで終了します。

拡張されたGitフローでアクティブリリース中にホットフィックスを実行すると、Gitコミットグラフが変化します。開始グラフは、コミットの最長行として展開され、メインは2つのコミットを1つのコミットで分岐し、その前に3つのコミットを分岐し、ブランチはv2020-09-18のタグが付けられた1つのコミットで分岐します。上記の最初の2つのステップの後、グラフには、mainが以前あった場所に新しいリリースがあり、mainはv2020-09-18でもありません。その後、残りの手順が実行され、最終的なグラフが作成されます。ここで、mainは、新しいリリースがあった場所よりも前のコミットであり、v2020-09-20は、v2020-09-18があった場所よりも前のコミットです。

適切な計画、十分に高いコード品質、および健全な開発とQA文化があれば、チームがこの方法を使用する必要はほとんどありません。 万が一に備えて、Gitフローを強化するためにこのような災害計画を作成してテストすることは賢明でしたが、実際に使用する必要はありませんでした。

強化されたGitフローに加えてCI/CDをセットアップ

すべてのプロジェクトに専用の開発環境が必要なわけではありません。 各開発者のマシンに洗練されたローカル開発環境をセットアップするのは簡単かもしれません。

しかし、専用の開発環境は、より健康的な開発文化に貢献することができます。 開発ブランチでテストを実行し、テストカバレッジを測定し、複雑さのメトリックを計算すると、多くの場合、ステージングに至る前にミスをキャッチすることで、ミスのコストを削減できます。

強化されたGitフローと組み合わせると、いくつかのCI/CDパターンが特に役立つことがわかりました。

  • 開発環境が必要な場合は、CIをセットアップして、開発ブランチへのコミットごとにビルド、テスト、およびデプロイします。 あなたがそれを持っていて、あなたの場合にそれが理にかなっているなら、ここでもE2Eテストに適合してください。
  • メインブランチへのコミットごとに、ステージング環境を構築、テスト、およびデプロイするようにCIをセットアップします。 この時点でも、E2Eテストは非常に有益です。
    • 両方の場所でE2Eテストを使用することは冗長に思えるかもしれませんが、修正プログラムは開発では発生しないことに注意してください。 mainへのコミットでE2Eをトリガーすると、修正プログラム日常の変更がなくなる前にテストされますが、開発へのコミットでトリガーすると、バグが早期に検出されます。
  • チームが手動の要求に応じてメイン環境から本番環境にビルドをデプロイできるようにCIを構成します。

ステージング(「ステージ」)と本番環境(「本番」)に加えて、開発環境(「開発」)がある場合に、Gitフローが強化されたCI/CDの推奨セットアップ。開発ブランチでのすべてのコミットにより、ビルドが開発にデプロイされます。同様に、メインへのすべてのコミットにより、ビルドがステージにデプロイされます。メインからの特定のコミットを手動で要求すると、ビルドが本番環境にデプロイされます。

このようなパターンは比較的単純ですが、日常の開発操作をサポートする強力な機械を提供します。

強化されたGitフローモデル:改善と考えられる制限

強化されたGitフローはすべての人に適しているわけではありません。 それは本枝を押す力の物議を醸す戦術を活用しているので、純粋主義者はそれを憤慨するかもしれません。 しかし、実用的な観点からは、何も問題はありません。

前述のように、修正プログラムはリリース中はより困難ですが、それでも可能です。 あまり頻繁に発生しないQA、テストカバレッジなどに適切な注意を払うことで、私の観点からは、従来のGitフローと比較した拡張Gitフローの全体的な利点との有効なトレードオフになります。 大規模なチームや、修正プログラムがより頻繁に発生する可能性のあるより複雑なプロジェクトで、強化されたGitフローがどのように機能するかを知りたいと思います。

強化されたGitフローモデルに関する私の前向きな経験は、主にクローズドソースの商用プロジェクトを中心に展開しています。 プルリクエストがソースツリーの古いリリースの派生に基づいていることが多いオープンソースプロジェクトでは、問題が発生する可能性があります。 それを整理するための技術的な障害はありません。予想以上の労力が必要になる可能性があります。 このような場合の拡張Gitフローの適用性に関して、オープンソーススペースで多くの経験を持つ読者からのフィードバックを歓迎します。

強化されたGitフローの背後にあるアイデアを開発する上で重要な役割を果たしてくれた、Toptalの同僚であるAntoinePhamに特に感謝します。


Toptal Engineeringブログでさらに読む:

  • トランクベースの開発とGitフロー
  • プロ向けのGitワークフロー:優れたGitガイド

マイクロソフトゴールドパートナーバッジ。

マイクロソフトゴールドパートナーとして、Toptalはマイクロソフトエキスパートのエリートネットワークです。 必要なときに必要な場所で正確に、必要な専門家と一緒に高性能のチームを構築しましょう!