#目的
- reset と rebaseの使い所を知る
#前提
ここでいう「美しい」のレベル感は、
どの機能がいつ開発および変更されたのかが 一目でわかる というレベル
#ストーリー
- 我々は少人数のチームで開発をしている。
- Gitは取り入れ始めたところ。Git flowでのブランチ運用も始めたばかり。
- こまめにコミットする文化なので、追加や変更がどこで入ったのか歴史を追うのが辛くなってきた。
- 問い合わせや障害にスピーディーに対応できるように歴史をスッキリさせる方法を模索中だ。
#準備
##リポジトリはこちら
gitお勉強用ブランチ
https://github.com/gitWMoon/git-study
##ローカルの状態
現在のブランチはdevelopとmasterの2つだけ
$ git branch
* develop
master
##リモートの状態
リモートもdevelopとmasterのみ
$ git branch -r
origin/HEAD -> origin/develop
origin/develop
origin/master
##ブランチ
Git flowなので以下のブランチが存在するが、今回登場するのはdevelopとfeatureである。
master: 唯一無二で即本番リリースできる資産。
develop: 次期リリース候補の資産。開発中はdevelopを更新していく。
feature: 実際の開発はfeatureブランチで行う。開発が終わったらdevelopにマージされる。
release: リリース準備をするためのブランチ。バージョンとか上げる。
hotfix: 緊急対応するためにmasterを親として作成されるブランチ。対応後はmasterとdevelopにマージされる。
#美しい歴史を作ってみよう
##featureの歴史を美しくする
まずはfeatureの歴史を美しくしてみよう。
B機能とC機能の修正をfeature/modify_B_Cというfeatureブランチで開発を行い、
次のような歴史ができたとする。
こまめにコミットする文化だと必然的に歴史が増える。
$ git log --pretty=format:"%H %cd %s" --graph
* 205831dbc1bbf7c94d5e0e998cbf647601d425c0 Sat Jul 9 13:31:05 2016 +0900 C-view 修正
* 3919186034691d3bc777559588fc91f67ffc2ee4 Sat Jul 9 13:30:11 2016 +0900 C-test修正
* 3985567bf9457ead384f93dc2032e82fc2910d0f Sat Jul 9 13:29:40 2016 +0900 C-api 修正
* 2a2021688f1e0629e1cf6ac93cd7b579ea7a115c Sat Jul 9 13:28:34 2016 +0900 B-test修正
* 657b145036ceec5af87d2491f6304283d19d8c30 Sat Jul 9 13:27:55 2016 +0900 B-view 修正
* 3c0c733ee4d6a10c4e7ac78a1624a2ff3fbf4d56 Sat Jul 9 13:27:02 2016 +0900 B-api 修正
* 25ff51a94a069d7580db0dfbd23dabb6e00743dc Sat Jul 9 13:25:13 2016 +0900 B機能、C機能修正 初期コミット
* 92dedb434a96136dfb45c66ef880fbac520c7d1b Sat Jul 9 13:07:44 2016 +0900 C機能開発完了 Merge branch 'feature/C-feature' into develop
###歴史をリセット
このままfeatureを終了すると今までと何ら変わらないので、次のコマンドで歴史をリセットしてみる。
初期コミットを指定、オプションは--softを指定。
$ git reset --soft 25ff51a94a069d7580db0dfbd23dabb6e00743dc
開発した内容はそのまま残り歴史だけがリセットされる。
(HEADが過去に移動する。)
$ git log --pretty=format:"%H %cd %s" --graph
* 25ff51a94a069d7580db0dfbd23dabb6e00743dc Sat Jul 9 13:25:13 2016 +0900 B機能、C機能修正 初期コミット
* 92dedb434a96136dfb45c66ef880fbac520c7d1b Sat Jul 9 13:07:44 2016 +0900 C機能開発完了 Merge branch 'feature/C-feature' into develop
$ git status
On branch feature/modify_B_C
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: B-feature/api/B-api
modified: B-feature/test/B-test
modified: B-feature/view/B-view
modified: C-feature/api/C-api
modified: C-feature/test/C-test
modified: C-feature/view/C-view
###新しい歴史を作る
では、改めてコミットしよう。
$ git commit
[feature/modify_B_C cd3ed4b] B機能、C機能修正完了 レビュー済み、レビュー指摘反映済み
6 files changed, 51 insertions(+)
$ git log --pretty=format:"%H %cd %s" --graph
* cd3ed4b8d6ac73e85b803378a41d2e4f6551e865 Sat Jul 9 13:39:40 2016 +0900 B機能、C機能修正完了 レビュー済み、レ
ビュー指摘反映済み
* 25ff51a94a069d7580db0dfbd23dabb6e00743dc Sat Jul 9 13:25:13 2016 +0900 B機能、C機能修正 初期コミット
なんと、スッキリした歴史になった!
途中の経過や、対応内容はコミット時のコメントで残せばいいのではないかと思う。
featureを終了し、developにマージすると開始と完了の歴史のみとなった。
$ git log --pretty=format:"%h %cd %s" --graph
* fd2ba4b Sat Jul 9 13:43:05 2016 +0900 Merge branch 'feature/modify_B_C' into develop
|\
| * cd3ed4b Sat Jul 9 13:39:40 2016 +0900 B機能、C機能修正完了 レビュー済み、レビュー指摘反映済み
| * 25ff51a Sat Jul 9 13:25:13 2016 +0900 B機能、C機能修正 初期コミット
|/
* 92dedb4 Sat Jul 9 13:07:44 2016 +0900 C機能開発完了 Merge branch 'feature/C-feature' into develop
どの機能がどのタイミングで開発または変更されたかを追いたい場合にはこのレベルの歴史で十分だろう。
##developの歴史を美しくする
今度はdevelopの歴史を美しくしてみよう
並行していくつかのfeatureブランチで開発を行ったあと、いつも通りdevelopにマージするとだいたいこのような歴史になる。
ぱっと見ようわからん。
$ git log --graph --pretty=format:"%h %cd %s"
* b28ce5d Sat Jul 9 16:13:51 2016 +0900 Merge branch 'feature/feature3' into develop
|\
| * 4f3bdd6 Sat Jul 9 16:10:48 2016 +0900 C-test C-view修正
| * 1cf2e74 Sat Jul 9 16:05:42 2016 +0900 C-api修正
* | 517b02e Sat Jul 9 16:13:17 2016 +0900 Merge branch 'feature/feature1' into develop
|\ \
| * | 51f560b Sat Jul 9 16:08:02 2016 +0900 hoge2 修正
| * | 63f4e03 Sat Jul 9 16:03:11 2016 +0900 fuga-test修正
| * | 85a39fa Sat Jul 9 16:02:36 2016 +0900 fuga-api 修正
| |/
* | ea5a6b8 Sat Jul 9 16:12:51 2016 +0900 Merge branch 'feature/feature2' into develop
|\ \
| * | 00606db Sat Jul 9 16:09:22 2016 +0900 B-view B-test修正
| * | 91d7a30 Sat Jul 9 16:04:23 2016 +0900 B-api修正
| |/
* | a0289f5 Sat Jul 9 16:12:21 2016 +0900 Merge branch 'feature/feature4' into develop
|\ \
| |/
|/|
| * 820e878 Sat Jul 9 16:00:55 2016 +0900 D-test D2-test 修正
| * 1ca8b89 Sat Jul 9 15:59:42 2016 +0900 D-view D2-viewの修正
| * 942242b Sat Jul 9 15:58:42 2016 +0900 D-api D2-api修正
|/
* 6f41152 Sat Jul 9 15:45:49 2016 +0900 Merge branch 'feature/rebase2' into develop
###featureの歴史をリセットし、再作成する
4本のfeatureを並行して開発しているとして、
先程同様にシンプルなfeatureブランチの歴史を作る。
$ git log --graph --pretty=format:"%h %cd %s"
* 339911c Sat Jul 9 16:28:09 2016 +0900 a-feature 開発完了
* ff4643d Sat Jul 9 16:25:13 2016 +0900 a-feature 初期コミット
このような歴史が4本あると思って欲しい。
###リベースする
まず、a-featureとb-featureは普通にマージしてfeatureを終了してみた。
git flow feature finish <branch_name>
これはいつも通りのdevelopのマージの歴史である。
$ git log --graph --pretty=format:"%h %cd %s"
* cc1a695 Sat Jul 9 16:39:36 2016 +0900 Merge branch 'feature/b-feature' into develop
|\
| * b3318af Sat Jul 9 16:29:55 2016 +0900 b-feature 開発完了
| * ea036c5 Sat Jul 9 16:25:45 2016 +0900 b-feature 初期コミット
* | 6fc7761 Sat Jul 9 16:39:10 2016 +0900 Merge branch 'feature/a-feature' into develop
|\ \
| |/
|/|
| * 339911c Sat Jul 9 16:28:09 2016 +0900 a-feature 開発完了
| * ff4643d Sat Jul 9 16:25:13 2016 +0900 a-feature 初期コミット
|/
* b28ce5d Sat Jul 9 16:13:51 2016 +0900 Merge branch 'feature/feature3' into develop
今度は、c-featureとd-featureはリベースのため-rオプション指定する。
git flow feature finish -r <branch_name>
歴史の分岐と合流が無くなり、1本になった。
$ git log --graph --pretty=format:"%h %cd %s"
* 6c45b01 Sat Jul 9 16:40:21 2016 +0900 d-feature 開発完了
* 02ef5f0 Sat Jul 9 16:39:55 2016 +0900 c-feature 開発完了
* cc1a695 Sat Jul 9 16:39:36 2016 +0900 Merge branch 'feature/b-feature' into develop
|\
| * b3318af Sat Jul 9 16:29:55 2016 +0900 b-feature 開発完了
| * ea036c5 Sat Jul 9 16:25:45 2016 +0900 b-feature 初期コミット
* | 6fc7761 Sat Jul 9 16:39:10 2016 +0900 Merge branch 'feature/a-feature' into develop
|\ \
| |/
|/|
| * 339911c Sat Jul 9 16:28:09 2016 +0900 a-feature 開発完了
| * ff4643d Sat Jul 9 16:25:13 2016 +0900 a-feature 初期コミット
|/
* b28ce5d Sat Jul 9 16:13:51 2016 +0900 Merge branch 'feature/feature3' into develop
超スッキリ!
いつどの機能が開発および変更されたかはこの歴史を見れば一目瞭然。
ただ、その後何度がリベースを行ったみたが若干挙動の違いがあるようで。
リベースでも枝が1本出て普通のマージみたいな歴史になることもあるようだ。
どういう条件でこうなるのか、まだ理解不足のようである。
$ git log --graph --pretty=format:"%h %cd %s"
* 4f9a66f Sat Jul 9 17:25:07 2016 +0900 add files jjjj kkkk llll
* 5fcf24e Sat Jul 9 17:24:37 2016 +0900 Merge branch 'feature/f2' into develop
|\
| * acc8034 Sat Jul 9 17:24:37 2016 +0900 rename iiii
| * d542b0b Sat Jul 9 17:24:37 2016 +0900 rename hhhh
| * a097802 Sat Jul 9 17:24:37 2016 +0900 rename gggg
|/
* 5aa3f13 Sat Jul 9 17:23:46 2016 +0900 Merge branch 'feature/f1' into develop
|\
| * d1b14a2 Sat Jul 9 17:23:46 2016 +0900 rename ffff
| * 0a3f2b6 Sat Jul 9 17:23:46 2016 +0900 rename eeee
| * 4b94a3e Sat Jul 9 17:23:46 2016 +0900 rename dddd
|/
* fbc249b Sat Jul 9 17:22:07 2016 +0900 Merge branch 'hotfix/hotfix2' into develop
いずれにしてもこの程度ならdevelopの歴史は一目瞭然である。
#まとめ
- リセットやリベースは今回のような目的で使うと効果がありそう。
- コミット時のコメント等はしっかり残したほうがいい。
- 美しいコードだけでなく、美しい歴史にも気を配ろう。