#pyfes で git-daily について話してきました
git-daily について #pyfes で話してきました。
- git-daily: git-daily
- #pyfes: Python Developers Festa 2011.10 を開催します - Twisted Mind
Webアプリケーション開発におけるGitのブランチ運用戦略についての話です。
パッケージものの開発などと大きく異なるのはやはりリリース頻度や、リリース環境です。デプロイサーバが登場するとかもそういうことですね *1
ちなみにこのへんの、概要的な話は、GREEのころエンジニアブログにも書きましたので、そちらを参照ください。
発表資料
SpeakerDeck におきました (はてなに埋め込めるようにならないかなー)
demo の内容
demoはコマンドの切り替えが速すぎて流れが読めない的な感じになってしまってすみません。良い機会なので、図と一緒にまとめようかと思います。
前提とストーリー
以下の話はgitflowのブランチモデルを採用したgit-dailyでのリリース作業のフローですね。
前提:
- 開発者A (a_dev) さんと開発者B (b_dev)、deployサーバ(deploy)、Gitリポジトリサーバ(server)がいます
- Gitサーバは、GitHub でも gitosis でも代わりません。bareリポジトリを保持していてアカウント管理されているリモートのサーバだと思ってください
- deployサーバで、リリースマネージャ的な人が、リリースブランチ切ったりmasterにマージする人だったりします。
ストーリー:
- Aさんがリポジトリを作成し、serverにそれをcloneします
- Aさん、Bさんはdevelopブランチで開発を進めます
- developブランチの開発がある程度まとまってリリースしたいという状況になります
- deployサーバで、リリースマネージャ的な人がリリース作業を開始します。
- リリース前に、修正が必要なことがわかりました。Bさんが手元で修正してリリースブランチにpushします。
- 修正が完了し、リリースブランチをproductionにデプロイしました。その後、deployサーバでreleaseを閉じる作業をします。
- 各開発者は、syncすることでremoteで閉じられたリリースブランチを、ローカルから掃除します。
※コマンドのログ中にある右に出てるのはRPROMPTです。ディレクトリ名とかブランチ名が出てます。
リポジトリの作成とserverへのclone
まず基本的なところですが、Aさんがリポジトリを作成してserverにcloneします。serverにclone するところですが、serverやら、Bさんの開発環境などは、すべて別のサーバにある前提です。
今回は、demoシミュレーションについては、同じサーバ内でやりますが、ディレクトリがそれぞれの開発環境/サーバに対応すると思ってください。また、RPROMPTにディレクトリ名出しておくので、cd とか書いてないのにディレクトリ移動してる場合とかはそっちを見て判断してください(とかいう)
デモに用いるディレクトリ。最初は空です。
% ls -la [~/tmp/gitdaily-demo] 合計 8 drwxr-xr-x 2 sotarok sotarok 4096 2011-10-15 16:10 . drwxr-xr-x 50 sotarok sotarok 4096 2011-10-15 13:59 ..
Aさんがリポジトリを作成
% mkdir a_dev [~/tmp/gitdaily-demo] % cd a_dev [~/tmp/gitdaily-demo] % git init [tmp/gitdaily-demo/a_dev] Initialized empty Git repository in /home/sotarok/tmp/gitdaily-demo/a_dev/.git/ % touch README [tmp/gitdaily-demo/a_dev @master] % git add README [tmp/gitdaily-demo/a_dev @master] % git commit -m "initialize" [tmp/gitdaily-demo/a_dev @master] [master (root-commit) d7a313d] initialize 0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 README
これでリポジトリが作成されました。今回は、gitosisなどを用いないため、server というディレクトリ名でmirrorを作成します。--mirrorは、bareリポジトリとしてcloneするという意味です。
% git clone --mirror a_dev ./server [~/tmp/gitdaily-demo] Cloning into bare repository ./server... done. % ls server [~/tmp/gitdaily-demo] HEAD config hooks objects refs branches description info packed-refs
Aさんがこれをoriginとしてremoteに追加します。これで、なんとなくGitHub <-> 開発者、とか、社内のGitサーバ <-> 開発者、の関係性がイメージ出来る状態になったと思います。
% cd a_dev [~/tmp/gitdaily-demo] % git remote add origin ../server [tmp/gitdaily-demo/a_dev @master] % git remote [tmp/gitdaily-demo/a_dev @master] origin % git fetch origin [tmp/gitdaily-demo/a_dev @master] From ../server * [new branch] master -> origin/master % git branch -a [tmp/gitdaily-demo/a_dev @master] * master remotes/origin/master
developブランチの作成とBさんの開発への参加
develop ブランチは master ブランチからの派生で作ります。また、それをoriginにもpushします。
% git branch -a [tmp/gitdaily-demo/a_dev @master] * master remotes/origin/master % git checkout -b develop [tmp/gitdaily-demo/a_dev @master] Switched to a new branch 'develop' % git push -u origin develop [tmp/gitdaily-demo/a_dev @develop] Total 0 (delta 0), reused 0 (delta 0) To ../server * [new branch] develop -> develop Branch develop set up to track remote branch develop from origin.
続いて、Aさんのローカルで、git daily initしておきます。masterとかどうする?とか聞かれますが、ここで他のブランチ名を指定することも可能です。何も入力しなければ、デフォルトです。
% git daily init [tmp/gitdaily-demo/a_dev @develop] Your remote is [origin] Name master branch [master]: Name develop branch [develop]: git-daily completed to initialize.
続いて、Bさんの環境も整えます。Bさんは、最初から server から clone してきます。また、git daily init もしておきます。
% git clone server ./b_dev [~/tmp/gitdaily-demo] Cloning into ./b_dev... done. % cd b_dev [~/tmp/gitdaily-demo] % git log [tmp/gitdaily-demo/b_dev @master] commit d7a313d863aa1938d4aff0caf43460e30562ffb7 Author: Sotaro KARASAWA <sotarok@crocos.co.jp> Date: Sat Oct 15 16:11:16 2011 +0900 initialize % git daily init [tmp/gitdaily-demo/b_dev @master] Your remote is [origin] Name master branch [master]: Name develop branch [develop]: git-daily completed to initialize. % git branch -a [tmp/gitdaily-demo/b_dev @develop] * develop master remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/master
また、deployサーバという位置づけで、 deployというディレクトリにも clone を作って git daily init しておきます。(結果省略)
% git clone server ./deploy [~/tmp/gitdaily-demo] % cd deploy [~/tmp/gitdaily-demo] % git daily init [tmp/gitdaily-demo/deploy @master] % git branch -a [tmp/gitdaily-demo/deploy @develop] * develop master remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/master
ここまでの状況を図にすると以下のようなかんじです。(a_dev意外のリポジトリ内部の構成は省略)
リリースしたい状況、deployサーバでrelease作業の開始
developブランチで開発がすすめられてしばらくたったとしましょう。リリースしても良いかな、と思える状況になったとします。
以下はAさんの手元のdevelopブランチです。開発中、originへのpush/pullは適宜行われているものとします。この例では、--decorateしてみてmasterから、開発が2コミット分進んでいることがわかります。
% git log --oneline --graph --all --decorate * efbe37b (HEAD, origin/develop, develop) modify comment * 107be63 added sample * d7a313d (origin/master, master) initialize
さて、deployサーバで、リリースマネージャ的な人が、release openします。現在は、まだ一番最初にcloneしただけの状態です。
% cd deploy [~/tmp/gitdaily-demo] % git log --oneline --graph --all --decorate [~/tmp/gitdaily-demo/deploy @develop] * d7a313d (HEAD, origin/master, origin/develop, origin/HEAD, master, develop)
git daily release open すると、日付でreleaseブランチが作成され、自動的にremoteにpushされます。あ、ちなみに最新のdevelopがremoteから取得された後openされます。
% git daily release open [tmp/gitdaily-demo/deploy @develop] [INFO] first, fetch remotes Confirm: create branch release/20111015-1635 from develop ? [yN] : y [INFO] merge develop branch from remote [INFO] create release branch: release/20111015-1635 [INFO] push to remote: origin To /home/sotarok/tmp/gitdaily-demo/server * [new branch] release/20111015-1635 -> release/20111015-1635 release opened % git branch -a [tmp/gitdaily-demo/deploy @release/20111015-1635] develop master * release/20111015-1635 remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/master remotes/origin/release/20111015-1635 % git log --oneline [tmp/gitdaily-demo/deploy @release/20111015-1635] efbe37b modify comment 107be63 added sample d7a313d initialize
この状態で、デプロイサーバから、stagingサーバなどへdeployし、確認作業などをするわけですね。以下では、たとえば capistrano (いや実際設定とか作ってないけど)
% cap staging deploy [tmp/gitdaily-demo/deploy @release/20111015-1635]
releaseブランチ上で修正作業
さて、staging環境などでテストをしていたら、このままではリリースできない、修正したいなー、という状況が発生しました。
この修正はBさんが担当することにします。
Bさんは、最初、release ブランチをローカルにもっていません。remoteにしかない状態ですね。
% git branch -a [tmp/gitdaily-demo/b_dev @develop] * develop master remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/master remotes/origin/release/20111015-1635
ここで、git daily release sync します。すると、remoteにあるreleaseブランチがローカルのブランチとしてチェックアウトされます。
% git daily release sync [tmp/gitdaily-demo/b_dev @develop] [INFO] first, fetch remotes [INFO] cleanup remote [INFO] checkout and tracking release/20111015-1635 Switched to a new branch 'release/20111015-1635' Branch release/20111015-1635 set up to track remote branch release/20111015 -1635 from origin. start to tracking release branch % git branch -a [tmp/gitdaily-demo/b_dev @release/20111015-1635] develop master * release/20111015-1635 remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/master remotes/origin/release/20111015-1635
修正作業をし、コミットしたらもう一度 sync することで、push されます。ログを確認すると、ローカルのほうが1つだけコミットが進んでいる状態ですね(releaseブランチに、1つ修正コミット)
% git status [tmp/gitdaily-demo/b_dev @release/20111015-1635] # On branch release/20111015-1635 # Your branch is ahead of 'origin/release/20111015-1635' by 1 commit. # nothing to commit (working directory clean) % git log --oneline --decorate aba974f (HEAD, release/20111015-1635) fixed: not PHP but Python efbe37b (origin/release/20111015-1635, origin/develop) modify comment 107be63 added sample d7a313d (origin/master, origin/HEAD, master, develop) initialize % git daily release sync [tmp/gitdaily-demo/b_dev @release/20111015-1635] [INFO] first, fetch remotes [INFO] cleanup remote [INFO] git pull [INFO] run git pull origin release/20111015-1635 From /home/sotarok/tmp/gitdaily-demo/server * branch release/20111015-1635 -> FETCH_HEAD Already up-to-date. pull completed [INFO] git push [INFO] run git push origin release/20111015-1635 To /home/sotarok/tmp/gitdaily-demo/server efbe37b..aba974f release/20111015-1635 -> release/20111015-1635 push completed
これで release ブランチの修正が完了したので、deployサーバで再度 sync してその修正を取得し、再度stagingなどにdeploy、確認作業などをします。
% git daily release sync [tmp/gitdaily-demo/deploy @release/20111015-1635] [INFO] first, fetch remotes [INFO] cleanup remote [INFO] git pull [INFO] run git pull origin release/20111015-1635 From /home/sotarok/tmp/gitdaily-demo/server * branch release/20111015-1635 -> FETCH_HEAD Updating efbe37b..aba974f Fast-forward sample.php | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) pull completed [INFO] git push [INFO] run git push origin release/20111015-1635 Everything up-to-date push completed % cap staging deploy [tmp/gitdaily-demo/deploy @release/20111015-1635]
確認が完了し、リリース作業を終了します
statingで確認が完了し、productionにデプロイします。
% cap production deploy [tmp/gitdaily-demo/deploy @release/20111015-1635]
deployサーバで、release close します。まずログ確認。
% git log --oneline --decorate --graph * aba974f (HEAD, origin/release/20111015-1635, release/20111015-1635) fixed * efbe37b (origin/develop, develop) modify comment * 107be63 added sample * d7a313d (origin/master, origin/HEAD, master) initialize
さて、closeです。
% git daily release close [INFO] first, fetch remotes [INFO] diff check [INFO] checkout master and merge release/20111015-1635 to master [INFO] run git pull origin master From /home/sotarok/tmp/gitdaily-demo/server * branch master -> FETCH_HEAD Already up-to-date. pull completed Merge made by recursive. sample.php | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) create mode 100644 sample.php [INFO] push master to origin To /home/sotarok/tmp/gitdaily-demo/server d7a313d..f62dba1 master -> master [INFO] first, fetch remotes [INFO] diff check [INFO] checkout develop and merge release/20111015-1635 to develop [INFO] run git pull origin develop From /home/sotarok/tmp/gitdaily-demo/server * branch develop -> FETCH_HEAD Already up-to-date. pull completed Merge made by recursive. sample.php | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) [INFO] push develop to origin To /home/sotarok/tmp/gitdaily-demo/server efbe37b..b5a59b7 develop -> develop [INFO] delete branch: release/20111015-1635 Deleted branch release/20111015-1635 (was aba974f). To /home/sotarok/tmp/gitdaily-demo/server - [deleted] release/20111015-1635 release closed
たくさん出力されたのでよくわからないことになってるかもしれませんが、ブランチ、ログは以下のようになっています。
% git branch -a [tmp/gitdaily-demo/deploy @develop] * develop master remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/master % git log --oneline --decorate --graph --all * b5a59b7 (HEAD, origin/develop, develop) Merge branch 'release/20111015- |\ | | * f62dba1 (origin/master, origin/HEAD, master) Merge branch 'release/ | | |\ | | |/ | |/| | * | aba974f fixed: not PHP but Python |/ / * | efbe37b modify comment * | 107be63 added sample |/ * d7a313d initialize
ブランチに --no-ff で develop と master に merge され、releaseブランチが掃除されていることがわかると思います。
これでリリース作業が完了しました。
各自の開発環境の掃除
deployサーバ上でリリースが完了しました。が、ここはGitの特徴ですが、remoteでリリースブランチが掃除されてもローカルには残ってるわけです。これを掃除しようとおもいます。
ここでも release sync が登場します。
Bさんは、先程修正作業のためにローカルにreleaseブランチを取得しましたので、以下の状態になっています。
% git branch -a [tmp/gitdaily-demo/b_dev @release/20111015-1635] develop master * release/20111015-1635 remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/master remotes/origin/release/20111015-1635
↑はまだfetch前の状態なので、remoteで消えたはずのreleaseブランチが、まだあります。
syncすればそのへんは適当に取得してやってくれます。
% git daily release sync [tmp/gitdaily-demo/b_dev @release/20111015-1635] [INFO] first, fetch remotes [INFO] cleanup remote [INFO] release closed! so cleanup local release branch [INFO] checkout develop [INFO] run git pull origin develop From /home/sotarok/tmp/gitdaily-demo/server * branch develop -> FETCH_HEAD Updating d7a313d..b5a59b7 Fast-forward sample.php | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) create mode 100644 sample.php pull completed [INFO] delete release/20111015-1635 Deleted branch release/20111015-1635 (was aba974f). Closed old release branch sync to release close
ブランチとログを確認すると:
% git branch -a [tmp/gitdaily-demo/b_dev @develop] * develop master remotes/origin/HEAD -> origin/master remotes/origin/develop remotes/origin/master % git log --oneline --decorate --graph [tmp/gitdaily-demo/b_dev @develop] * b5a59b7 (HEAD, origin/develop, develop) Merge branch 'release/20111015... |\ | * aba974f fixed: not PHP but Python |/ * efbe37b modify comment * 107be63 added sample * d7a313d (master) initialize
これで、release ブランチがお掃除されて、developに戻りました。 *2
作業の流れのまとめ
これで一連の作業が完了しました。
簡単に普段の作業のまとめをします:
- develop ブランチで開発作業をする
- release 作業をはじめるとdevelopブランチからブランチが切られる (release open)
- release ブランチで最終確認中、修正が必要ならreleaseブランチで修正作業をする (release sync)
- 本番リリースできたら、releaseブランチをmergeします。 (release close)
- リリース後、各開発者は sync してお掃除とかする (release sync)
まとめと余計な話
- Python かいわいでの発表はじめてで緊張しました
- ちなみに、git-daily本体はgitflowを使って開発しています。
- スライド中でも話しましたが、Git運用のプラクティス、どんどん共有していきたいですね!
おまけ: zshの連携は、vsc_info使ってます。
autoload -Uz vcs_info zstyle ':vcs_info:*' enable git zstyle ':vcs_info:*' actionformats ' %F{3}@%F{4}%b%F{3} !!%F{1}%a%f' zstyle ':vcs_info:*' formats ' %F{3}@%F{4}%b%f' RPROMPT="%F{5}[%F{2}%3~\${vcs_info_msg_0_}%F{5}]%f"