1つの施策を複数人で開発する時のブランチ管理を図解した


環境

git version 2.14.1

はじめに

1つの施策を複数人で開発した際に、「どうやってビルドしようか?」とか「何かすごくコンフリクトするんだけど」とか、思うようにブランチ管理ができませんでした。その問題点と解決策のまとめになります。
以下を前提としています。

  • 施策のメインエンジニアが機能を切り出して他の人に作業を振る
  • レビュー会やデバッグを回すために頻繁に動作確認できる状態を更新する
  • チーム全体がrebase推奨派

先にまとめ

  • 施策全体に関わるものはbaseブランチにコミットする
  • 使い捨てのbuildブランチを作ってビルドする
  • rebaseで派生元ブランチの変更を取り込む
  • 歴史が変わったらrebase -iで不要な部分を捨てつつ取り込む

解説

施策全体に関わるものはbaseブランチにコミットする

 
※「施策名_base」「施策名_future」のように施策名が頭に付く想定です。
問題:担当を決めて一斉に取り掛かると実装内容が被ったり、基礎部分が無いまま実装したりと効率が悪い。
解決策:メインエンジニアが共通部分をざっくり実装して、そこから各機能ブランチを派生させる。

使い捨てのbuildブランチを作ってビルドする


問題:機能ごとにブランチを分けると動作確認できない。
解決策:全てをマージした動作確認用のビルドブランチを作成する。
どこかのブランチで歴史が変わるとbuildにマージする際にコンフリクトを起こしてしまうので、いつでも捨てることができるように日付けを付けています。

rebaseで派生元ブランチの変更を取り込む

問題:派生元の変更を取り込みたいがマージはしたくない。
解決策:rebaseを使う。
rebaseって?という方はこちら参照ください

1.(g)を取り込みたい

2._baseをdevelopでrebaseする

git checkout _base
git rebase develop

(c)(d)とは別ハッシュで(c')(d')が生まれ、(c)(d)は_future1にしか存在しなくなります。

3._future1を_baseでrebaseする

git checkout _future1
git rebase _base

(c)=(c')、(d)=(d')の内容であれば(c)(d)はログに残らず、綺麗になります。

歴史が変わったらrebase -iで不要な部分を捨てつつ取り込む

問題:rebaseしたら自分の実装外の箇所でコンフリクトが起きる。
解決策:rebaseの「-i」オプションを使う。
今のチームはコードレビューを投げる前にログを整理する習慣があります。
ところが、ログを改変するとrebaseで盛大にコンフリクトを起こしてしまいます。

1.開発中の状態

2.baseにsquashをかける

(c)(d)(e)はfuture1にしか存在しなくなります。

3(失敗).future1をbaseでrebaseする

(h)=(c')+(d')+(e')なのですが、別物として認識されます。
コンフリクトするだけでも問題なのですが、さらに無理矢理解消するとどちらもログに残ってしまいます。
そこで...

3(成功).future1をbaseでrebaseする際に不要な部分を捨てる

git checkout _future1
git rebase -i _base

rebaseに「-i」オプションをつけると下記のように各コミットをどうするか聞かれます。

pick d873898c3 (c)のコミットメッセージ
pick 3d1ef6711 (d)のコミットメッセージ
pick 963d665d8 (e)のコミットメッセージ
pick 853c53402 (f)のコミットメッセージ
pick 4b746e4f0 (g)のコミットメッセージ

ここで(h)に含まれている(c)(d)(e)をdrop(削除)します。
future1ブランチの初回コミット以前のコミットを削除するイメージです。

d d873898c3 (c)のコミットメッセージ
d 3d1ef6711 (d)のコミットメッセージ
d 963d665d8 (e)のコミットメッセージ
pick 853c53402 (f)のコミットメッセージ
pick 4b746e4f0 (g)のコミットメッセージ

こうすることで図のような綺麗なログになります。

さいごに

すごくすごく手探りです。
何か新しく問題が発生したり、より良い解決策があれば都度更新していきます!