Chapter 4.1 - Managing branches - What

一つのrepsitoryで管理されるcommit群は、一番最初のcommitをrootとしたツリー構造のようになっています。一連のcommitの履歴 (commit log) はbranchを切り出すことによって分岐し、マージ (merge) することによってその分かれたcommitの歴史を統合することができます。

branchの切り出しによって分岐したcommitの歴史は、ユーザーが任意のタイミングでbranchを切り替えながらcommitを重ねていくことでそれぞれ別々のcommitの歴史を持つことになります。Gitを使ったバージョン管理に不慣れなうちは、branchを切ってcommitの歴史を分岐させることにどんな意味やメリットがあるのか中々想像がつかないかもしれません。

分かりやすいように、あるソフトウェアをチームで開発しているという仮定で説明していきます。次のcommit logのようにソフトウェアのベースとなる機能は既に実装が終わっており、これから更に新しい機能を実装しようとしている状況です。

* 9fe47fb (HEAD, master) feat: add extension
* 15643db chore: add README
* 108c3e4 feat: implement base program

masterというのは、全てのrepositoryに自動的に作成されるツリーの幹のようなbranchです。HEADというのは、現在いるブランチの先頭のcommitを指す代名詞のようなものです。

ここから新しい機能を実装しようというとき、本来であれば過去のcommitと同じようにそのままコードを追加してそのままcommitしていけば多くのケースでは特に何も問題は無いでしょう。しかし、もし複数の機能を同時に並行して実装したいときはどうでしょうか。また、機能の実装と更に並行してバグの修正を行わなければならなくなった場合はどうでしょうか。

* 7fbef9a (HEAD, master) bugfix: catch exception in main
* 2195b01 fix: syntax error for issue1
* f889005 feat: implement Model for issue2
* 5cb0ae2 feat: implement Controller for issue2
* 47421a7 feat: implement Controller for issue1
* 9fe47fb feat: add extension
* 15643db chore: add README
* 108c3e4 feat: implement base program

愚直にcommitを重ねればこうなります。issue1の機能を実装しているときにissue2の機能のためのcommitが挟まったり、途中でベース機能の例外処理漏れのバグを修正したり、歴史が一直線なのでいつ何が起きてどうしたか分かりやすいかもしれませんが、ソフトウェアの開発の歴史としては機能やバグ単位で切り分けがされていないのは適切ではありません。

次の例では、上の問題をbranchの切り分けによってうまく整理できているcommit logを示します。

* cd051e5 (HEAD, fix/exception_main) bugfix: catch exception in main
| * 35a961d (feat/issue2) issue2: feat: implement Model
| * 82be355 issue2: feat: implement Controller
|/  
| * ae31340 (feat/issue1) issue1: feat: implement Controller
|/  
* 9fe47fb (master) feat: add extension
* 15643db chore: add README
* 108c3e4 feat: implement base program

feat/issue1, feat/issue2ではそれぞれ追加する機能の実装をし、fix/exception_mainではベース機能のバグを修正します。前述の通りそれぞれのbranchはそれぞれのcommitの歴史を持ち、ユーザーがmergeをしない限りはcommitが他のbranchに影響を及ぼすことはありません。そして、実装や修正が終わった順に順次masterにmergeしていきます。

*   d7842d1 (HEAD, master) Merge branch 'fix/exception_main'
|\  
| * cd051e5 (fix/exception_main) bugfix: catch exception in main
* |   9a021a3 Merge branch 'feat/issue2'
|\ \  
| * | 35a961d (feat/issue2) issue2: feat: implement Model
| * | 82be355 issue2: feat: implement Controller
| |/  
* | ae31340 (feat/issue1) issue1: feat: implement Controller
|/  
* 9fe47fb feat: add extension
* 15643db chore: add README
* 108c3e4 feat: implement base program

他のbranchがmasterに統合されるときには、Gitがそれぞれのbranchの差分を自動でチェックしその分だけをmasterに反映してくれます。Gitは可能な限り全自動で差分を統合してくれますが、例えばbranchの分岐先で重複する箇所がそれぞれ別の変更を加えられているような場合にはGitは優先すべき差分を判別できずにmergeが中断されてしまいます。こういった状態をコンフリクト (conflict, 衝突) と言い、ユーザーはconflictした箇所を手動で編集しcommitし直す必要があります。

conflictは、ユーザーがlocal repositoryでbranchをmergeしようとする時以外に、remote repositoryから最新のcommitをpullしようとするときにも発生する場合があります。チームで開発をしている場合、remote repositoryに存在する最新のcommit(=他人のcommit)を含めて自分の独断でconflictを解消する場合には慎重に作業するようにしましょう。

Naming rules of branches

branchにはユーザーが任意の名前をつけることができます。個人でrepositoryを運用する分には適当な名前でも問題は無いですが、基本的に第三者がbranch名を見た時に何のためのbranchなのか分かりやすい名前をつけるべきです。命名規則はチーム毎に統一されているケースが多いので、事前に確認しておきましょう。

results matching ""

    No results matching ""