読者です 読者をやめる 読者になる 読者になる

すこしふしぎ.

VR/HI系院生による技術ブログ.まったりいきましょ.(友人ズとブログリレー中.さぼったら焼肉おごらなきゃいけない)

【初心者向け】gitのbranch運用入門【git flow もどき】

git

どうもこんにちは.1000chです. facebookのおすすめ言語診断したらrubyがでてきました. ふだん蛇使いなのですが,宝石商としてのスキルも磨くべきなのでしょうか. いまんとこrubyインタラクティブシェルの起動方法も知らないレベルなんですけどね.

さて,最近チームで開発することが多々あり,ビギナーながらだんだんとgitをつかった開発に慣れてきました. 慣れるにつれ,なかなかわからなかったチーム開発におけるブランチの運用方法が見えてきた気がします.

ということで今回は初心者がgitを使う際一番最初にぶつかるであろう壁,ブランチ運用の仕方をgit-flowをベースにまとめます. 自分のような初心者向けということで,後ろにチュートリアル形式でまとめておこうと思います.

対象としてはgit commitはできるけどgit branchが怖い人,目指すところは

f:id:ism1000ch:20140331142050p:plain

f:id:ism1000ch:20140331142113p:plain

こうすることです!

それではいきましょう.

git flow モデル

gitを初めて使う身に取って一つの壁となるのが,ブランチの運用ではないかと思います. commitやらpull/pushといったコマンドは使う頻度が高いため,すぐ何となく使えるようになりますよね. 「とりあえずgit add .git commit」を繰り返せばいいんだろ,という感じで.

しかし,branchはどうでしょう.コマンドとしての使い方自体は簡単ですが,どのようなタイミングでブランチを切るのか?ということは,一度キチンと考えておかないと自然と身に付けることはなかなか難しいと思います. ...というか初めてgitに触れた段階では,そこまで気を向けることがまず難しいと思いますw コンフリクトへの恐怖もあるので.

個人で開発している分には別にそこまで気にしなくてもまぁいいか,と思いますが,チーム開発となるとそうも言ってられません. 皆が自由にコミット・プッシュをしてしまうと,なかなか残念なコミットログが待っています. 冒頭一枚目の画像なんかいい例ですね. このような状態では,後から振り返ることがどんどん難しくなっていく,バージョン管理のはずなのに,戻したいバージョンの場所がわからない,なんて残念なことになりかねません.

ある程度gitに慣れた時点でブランチ運用についてググってみたところ,git-flowモデルというモノを知りました. 基本的な考え方は以下の図です.

コレをみて「なるほど!」となった人はこの記事を読む必要がないと思いますw 存分にgit-flowモデルにのっとったブランチ運用をしてください.

しかしおそらくgit初心者では,「なるほどわからん!」となることでしょう. 実際自分もgitに初めて触れたときに同じ図を見たしたのですが,平行したブランチが多くて意味がわからず,静かにブラウザを閉じた記憶があります.

ここを参考に簡単に各ブランチの特徴をまとめると,

  • master

公開したバージョンを保存しておくブランチ. 基本的に安定バージョンをpullする時しか使いません.

  • develop

開発のベースとなるブランチ. 様々なブランチで行われた変更をまとめる役割を果たす. メンバーそれぞれが開発したブランチをマージしてゆく. release/hotfixによる変更もこちらに取り込みます.

  • feature

個別機能を実装していくブランチ. チケット駆動開発をする場合,チケットごとに1ブランチを作ると良さげ. 開発が終わったら,developブランチに取り込みます.

  • release

ある程度developが進んだ際,公開の準備をするブランチ. 不具合が発覚した場合,修正してdevelopに取り込みます.

  • hotfix

公開したmasterで発覚したバグ・不具合を修正するブランチ. 可及的速やかに解決すべきissueであり,fixされ次第master(現在公開中)とdevelopブランチに取り込みます.

いかがでしょうか.最初は取っ付きにくいかもしれませんが,基本的にdevelop->feature->developの流れを繰り返す,と覚えておけばだいたいは大丈夫だと思います. チケット駆動開発を念頭にgithubのissueなど,チケット管理ツールを利用すると良いでしょう.

また運用上で個人的にこうするといいかな,と思うのは

  • releaseブランチはfeatureブランチの一つを用いる

  • developブランチ自体に開発コミットをしない(マージコミットのみにする)

  • マージはgit merge --no-ff <branch_name>を使う

  • developが更新された場合,featureブランチでgit rebase developする

といった点です.

releaseブランチの役割はある程度開発が進んだ状態でリリースのための準備をすることとありますが,これは一つのチケットとしてfeatureブランチと同じように運用すればいいと思います. ブランチの種類が増えるとわかりにくくなってしまいますし.

またdevelopに開発コミットをしてしまうと,どのチケットの為のコミットかという情報が可視化されづらくなります.developはマージコミットだけにすることで,様々なチケット(ブランチ)のまとめ役,として使うとよいでしょう. 同様の理由で,マージでファストフォワードにすると,developに変更がなかったときにコミットが別ブランチにならず,developブランチ上の開発コミットとして見えてしまうため,マージは--no-ffオプションを強く推奨します. ただし,pullまで--no-ffをつけてしまうとローカルとリモートのdevelopが同じコミットを指さなくなるので,pullは--ffで行いましょう(やらかしたらgit reset HEAD~しましょう).

そしてリベースについてですが,developが変更された時点で行うと,余計なコンフリクトの可能性を軽減でき,また履歴としてどの機能をどのような順番で完成したかがわかりやすくなると思います.ただし,どのタイミングで実装が始まったかという情報の時系列はわかりにくくなってしまうので,そこは好みでわかれるところでしょうか. hotfixやreleseでバグが修正されたときのみrebaseしておく,というような使い分けがあってもいいかもしれないですね.ともあれチームで意見を統合することが大切だと思います. マージとリベースを紹介した時の記事も参考にしてみてください.

チュートリアル

では,実際にgit-flowモデルをもとにアレンジした開発の流れを擬似的に追ってみましょう. 世間にはgit-flowというgit flowに特化したコマンドもあるのですが,余計なコマンドを覚えるのは負担になりそうなので今回は使わないでおきます. 基本操作でgit-flowに慣れてから使うのがいいのではないかな,と思います.

開発開始

#新しく始める
[local] git init
[local:master] git remote add <name> <url> #name=originとする
[local:master] git checkout dev

#既存リポジトリ(git_flowで運用済み)から始める
[local] git clone url
[local:master] git checkout dev

機能を開発

# 新しいチケットに手を出す
[local:dev] git pull origin dev
[local:dev] git checkout -b <feature_name>

# 開発を行う
[local:feature_name] git add .
[local:feature_name] git commit -m "commit_message" 

# (+α)途中でdevelopが更新されたら
[local:feature_name] git checkout dev
[local:dev] git pull origin dev
[local:dev] git checkout feature_name
[local:feature_name] git rebase dev # feature_nameブランチの分岐元が最新のdevとなる

機能開発が一段落した

# ローカルでマージする場合
# devはリモートの最新をpull済み,
# feature_nameはrebase/動作確認済みとする
[local:feature_name] git checkout dev
[local:dev] git merge feature_name # rebase済みであればconflictはないはず.
[local:dev] git branch -d feature_name # 必要に応じてマージしたブランチは捨てる
[local:dev] git push origin dev


# githubでpull reqを使う場合
[local:feature_name] git push origin feature_name
[remote] pull request : develop <= feature_name

# どちらも同じようなコミットログになります.

開発が一段落した.デプロイしよう.

# 最新がdevにpush済みとする
[local:dev] git checkout master
[local:master] git pull origin master
[local:master] git push origin master

# githubでpull reqを使う場合
[remote] pull req : master <= dev

# 本番環境で
[server] git pull origin master

緊急バグが起きた!

# 1:バグ発生
# 初めてのバグの場合
[local:dev] git checkout -b hotfix origin/master

# hotfixが存在する場合
[local:dev] git checkout hotfix
[local:hotfix] git pull origin master


# 2:デバッグ終了:master/ developにhotfixをmerge
# ローカルでマージする場合
[local hotfix] git checkout master
[local master] git merge hotfix

[local hotfix] git checkout develop
[local develop] git merge hotfix

# githubでpullreq使う場合
[local hotfix] git push prigin hotfix
[remote] pull req : master <= hotfix
[remote] pull req : develop <= hotfix

まとめ

いかがでしたでしょうか. 実git歴約4ヶ月を経て,こんな感じで運用していけば良いのでは?という自分なりのフローを紹介してみました. まだまだ実際に運用した期間も短いので,「ここ違うのでは?」「こーゆー時はこうするべき!」などありましたらぜひ教えていただきたいと思います.

結論

とにかく手を動かして覚えましょう!