はじめに
オフショアを含めた複数人での並行開発を行っています。この際に運用していたGitでの開発フローについて自分用の忘備録としてまとめておきます。
おかしい点などがありましたら、コメントでご指摘頂ければ幸いです。
登場するブランチの種類
ブランチ名 | 説明 |
---|---|
master | 運用中のバージョンを管理するブランチ |
develop | 開発用のブランチ |
feature-xxx | developから派生する。開発の単位、機能単位で作成し、developにマージ後に削除する。 |
release-xxx | リリース前テスト用のブランチ。developから派生する。masterとdevelopにマージ後に削除する。 |
hotfix-xxx | 緊急バグ対応用ブランチ。masterから派生し、masterとdevelopにマージ後に削除する。 |
各ブランチの全体関連図
Gitでの開発フローについて
Gitでは、開発の段階に応じて次々とブランチを作成・削除しながらソースを保守していきます。これを「開発フロー」といいます。
Gitによる開発フローには「Gitflow」「GitHub Flow」「Git Feature Flow」など様々な方法が提唱されていますが、ここでは様々なイベントに柔軟に対応できる基本的なGitflowをベースにした開発フローを紹介します。
「複数の運用バージョン」があり得ない単一のウェブサイトの為のリポジトリの場合には、もっとシンプルなGit Feature Flowやそれに近い開発フローが似合っている場合が多いようです。
また、小さなチームでの開発の場合には、featureブランチを作らずに直接developブランチ(のローカルブランチ)上で開発を行い、テストを行ってからプルリクエストを投げる、というパターンもあるでしょう。
開発チームに合わせて適切な開発フローを選べばよいと思いますが、このGitflowをベースにした基本的なブランチ戦略を理解しておくと、例他の開発フローを採用している場合でも、様々なケースで応用が利くようになります。
注意事項
最近のGitは、masterではなくmainというブランチを規定で作成します。
各自の環境に合わせて、masterをmainと置き換えてお読みください。
なぜ運用ブランチ(master)と開発ブランチ(develop)を分けるのか
開発フローでは一般的に「運用ブランチ(master)」と「開発ブランチ(develop)」を分けて管理します。
なぜ運用ブランチと開発ブランチを分ける必要があるのでしょうか。
1つのmasterブランチ上で開発を続けてはいけないのでしょうか。
上記のようなブランチの更新を行っていると、ある時に「運用中のバージョンに緊急対応が必要になった」場合に困ることになります。
そもそも「現在運用中のバージョンはどこか?」が分かりにくいですし、未リリースの新機能の後ろに緊急バグ対応のコミットを行っても、それをそのままリリースすることはできません。
その為に、運用中のバージョンと開発中のバージョンは別のブランチとして管理しておくことが必要です。
※master … 運用ブランチ、develop … 開発ブランチ
運用中の緊急バグ対応(hotfixブランチ)
先ほどの例、「運用中のバージョンに緊急バグ対応が必要になった」についてもう少し考えてみます。
この時、開発ブランチに対してその緊急バグ対応をしても、その結果を運用中のバージョンには適用できません。
開発ブランチには未リリースの機能が追加されており、それに対してバグ対応をしても、運用ブランチに未リリースの機能を取り込むわけにはいかない為です。
※master … 運用ブランチ、develop … 開発ブランチ
運用ブランチに直接バグ対応をしても良いですが、そうすると、「さらに緊急のバグ対応」が出た時に、とっさに対応することができなくなります。
一般的にはこのような場合、「hotfixブランチ」をmasterブランチから派生させ、そこでバグ対応を行います。
hotfixブランチ上での動作確認が終わったら、それをmasterにマージし、運用環境に適用します。
その後、同じバグ対応を開発ブランチにもマージし、不要になったhotfixブランチを削除します。
こうしておくことで、運用中のバグ対応と運用ブランチをそれぞれ独立させることができる為、バグ対応中に別のより緊急のバグ対応が発生した場合にも、迅速に対応することができるようになります。
尚、上記のような場合、hotfix-1を再度masterからリベースしてhotfix-2を取り込み、テストを行ったあとmasterとdevelopにマージします。
マージ後は、不要になったhotfixブランチを削除し、代わりにタグを付けて「hotfixが行われたこと」をマーキングしておきます。
(※すいません、上記の図の「hotfix-1」と「hotfix-2」のタグ、逆ですね(汗)。先にhotfix-2、次にhotfix-1でした。図の修正に手が回らないので、注意書きとして追記しておきます)
機能ブランチ(featureブランチ)
まとまった機能単位での開発はdevelopブランチに直接コミットするのではなく、機能ブランチ(feature)をdevelopブランチから派生させ、そこで開発を行い、developにマージします。
こうすることで、feature-1の開発中の不安定な動作がfeature-2の開発に影響を及ぼさないようにできます。
developにはテスト済の機能のみをマージしていき、必要に応じてfeatureブランチに取り込んで開発を続けます。
共通機能の開発
ある機能の開発中に、共通機能として機能Zが必要になった場合、別のfeatureブランチとして独立させることにより、機能Zが必要な他の機能が機能Zのブランチのみを早期に取り込むことができるようになります。
手順としては、以下のように行います。
機能Zが単純な変更や改修だった場合、別のfeatureブランチとして独立させず、cherry-pickという方法で特定のコミットの差分のみをdevelopブランチに反映する方法もあります。
運用環境へのリリース前テスト(releaseブランチ)
開発ブランチ(develop)で追加した機能を運用ブランチ(master)にリリースする際には、リリース用のブランチ(release)を作成し、そこでリリース前テストを行います。
その都度リリースブランチを作るのではなく、stagingブランチを常設し、CI/CD機能でステージング環境への自動デプロイをしているケースもあると思いますが、おおまかな考え方としては同じです。
リリース用のブランチを作ってそこでテスト作業を行うことで、開発作業を継続しつつ、リリーステストで見つかったバグの対応を独立して行うことができます。
リリースが終わったら、masterとdevelopにそれぞれマージした後、リリースブランチを削除してバージョン名のタグを付けます。
おまけ:顧客別のカスタマイズ版を並行管理する
顧客別に異なるバージョンを管理する場合、次のように「ベース機能」と「カスタマイズ版」を異なるリポジトリで管理する方法があります。
サブモジュールを利用する方法もありますが、今回は直接各カスタマイズ版のリポジトリから、ベース機能のリポジトリの「base」ブランチを参照するようにします。
base_repo:ベース機能の共有リポジトリ
c1_repo:カスタマイズ版1の共有リポジトリ
c1_repoのローカルリポジトリ
c1_repoの開発を行う端末では、c1_repoをクローンして作業を行います。
この時、base_repoをリモートリポジトリとして追加し、base_repo/base の最新コミットを取得できるようにします。
git remote add base_repo <base_repoのリポジトリのURL>
つまり、このローカルリポジトリには、c1_repo(origin)とbase_repoの二つのリモートリポジトリが登録されます。
remote
origin
base_repo
base_repo/base の変更を c1_repo/base に取り込む
base_repo/base を取得して c1_repo/baseブランチに反映するには次のようにします。
git fetch base_repo # リモートのbase_repoリポジトリをフェッチ
git checkout base # ローカルのbaseブランチをチェックアウト
git merge base_repo/base # リモートのbase_repo/baseブランチをローカルのbaseにマージ(①)
git push origin base # c1_repoのリモートブランチにプッシュ (②)
c1_repo側のbaseへの変更をbase_repoに反映する
もし、c1_repo側でbaseブランチに変更を行っており、それをbase_repoに反映したい場合には、次のようにします。
git fetch base_repo … リモートのbase_repoリポジトリをフェッチ
git checkout base … ローカルのbaseブランチをチェックアウト
git merge base_repo/base … リモートのbase_repo/base ブランチをローカルのbaseにマージ(①)
git push origin base … c1_baseにプッシュ(②)
git push base_repo base … base_repoにプッシュ(③)