解決したい問題
色々試しながら実装してて、ふと気づくと...
ヤベェ差分がめちゃくちゃデカい。。。
なんとか小分けにできないものか?
どうしよう?
cherry-pick
とかで分けられないかと考えてみたものの、
試行錯誤しながらcommitしてたので、ログを見ても何を取ればいいのかさっぱり。
そんな折、先駆者を発見
https://brewing-bits.com/blog/splitting-big-merges/
これ参考に、git rebase -i
を使って細かくしてみる。
大まかな流れ
- 作業ブランチを新たに
master
から切る - 大量の変更の中から、何がしかひとまとまりの変更をピックアップして1のブランチに
commit
- PR(PullRequest)作成
- 今いるブランチをベースに新たにブランチを切る
- 同じようにひとまとまりの変更をピックアップし、
commit
- PR(PullRequest)作成
- 4〜6を繰り返し
使うコマンド
git log --oneline --reverse HEAD..huge_update_branch -- path/to/file/
で関係するコミットのhash commit-message
を取得。
git rebase -i HEAD
のエディタにコピペして、rebase
の各種コマンドで「このコミット使う」や「使わない」やを決める。
...まぁやってみよう
トライ
ここで、どでかい変更を加えたブランチをhuge_update_branch
とする。
まずはmasterからブランチを切る。
git checkout -b huge_update_branch_part1
ここにピックアップしたひとまとまりの変更を加える。
何をピックアップするか、diffを眺める。。。
末端のutility的なクラスからにしよう。
開始〜
git log --oneline --reverse HEAD..huge_update_branch -- path/to/util/
ababcdcd1 utilに新しいメソッド追加したよ
efefghgh2 ファイル名変えたよ
ijijklkl3 タイポ
大量変更があるブランチ(huge_update_branch
)と今のブランチ(huge_update_branch_part1
)との差分ログの中で、path/to/file/
以下のファイルに変更があったコミットログが一覧で出た。
これをrebase
で使う。
git rebase -i HEAD
1 noop
2
3 # Rebase abcdefg..abcdefg onto abcdefg (1 command)
4 #
5 # Commands:
6 # p, pick = use commit
7 # r, reword = use commit, but edit the commit message
8 # e, edit = use commit, but stop for amending
9 # s, squash = use commit, but meld into previous commit
10 # f, fixup = like "squash", but discard this commit's log message
11 # x, exec = run command (the rest of the line) using shell
12 # d, drop = remove commit
13 #
14 # These lines can be re-ordered; they are executed from top to bottom.
15 #
16 # If you remove a line here THAT COMMIT WILL BE LOST.
17 #
18 # However, if you remove everything, the rebase will be aborted.
19 #
20 # Note that empty commits are commented out
当然差分ないので、最初はこんな感じのが出る。
さっきのコミットログ貼る。
1 ababcdcd1 utilに新しいメソッド追加したよ
2 efefghgh2 あ、utilの新しいメソッドにコミットし忘れ
3 ijijklkl3 タイポ
4 # Rebase abcdefg..abcdefg onto abcdefg (1 command)
5 #
6 # Commands:
(以下略)
各コミットに対してコマンド決める。
1 pick ababcdcd1 utilに新しいメソッド追加したよ
2 fixup efefghgh2 あ、utilの新しいメソッドにコミットし忘れ
3 fixup ijijklkl3 タイポ
4 # Rebase abcdefg..abcdefg onto abcdefg (1 command)
5 #
6 # Commands:
(以下略)
一つ目のログだけ使う(pick
)ようにして、あとの二つは一つ目にがっちゃんこ(fixup
)。
次〜
あとは基本的にこれの繰り返し。
今いるブランチ(huge_update_branch_part1
)から新しいブランチを切る。
(ここあまりよくわかってない。master
からでも良いような気もする。)
git checkout -b huge_update_branch_part2
じゃあ次はmodel
への変更だけをピックアップ。
git log --oneline --reverse HEAD..huge_update_branch -- path/to/model/
同じように、rebaseのエディタにペロッと貼ってコマンド指定。
git rebase -i HEAD
1 pick ababcdcd1 modelのrecord変えたよ
2 pick efefghgh2 リネーム
3 fixup ijijklkl3 タイポ
4 drop mnmnopop4 もう一つリネーム
5 drop qrqrstst5 revert:もう一つリネーム
6 # Rebase abcdefg..abcdefg onto abcdefg (1 command)
7 #
8 # Commands:
(以下略)
以降繰り返して。。。
細かいPRに分けられたー!ヽ(´▽`)ノワーイ♪
全16弾に及ぶPR完成
これもこれで大変だけど、さくさくマージは進んだ。
(途中1000行越えもあるが、renameとかがメインなので数字ほどのインパクトはない)
感想など
- 変更の仕方によっては割とコンフリクト出る。
- 地道に解決するしかない。
- これ割と辛い。
- なので、ベストプラクティスは、「こんなことになる前に、なんとかしろ」
- テストコードへの変更は、最後のPRでまとめて直したのは楽で良かった。
- ということは、最後のPRまではビルドはコケ続ける。
- ので、いきなり
master
ではなく、develop
ブランチへ全てのPRを投げた。 - で、全部揃ってテストも通った段階で
master
へのPR。
- 「最終的にテスト通ればいいよね」は、ある程度テストカバレッジ無いと怖すぎる。
- 参考ページに書いてあった
git show-branch
は上手く使えなんだ。- 「大元のブランチと差分ないか確認するだけなら、
git diff
でもよくね?」と思って使わなかった。
- 「大元のブランチと差分ないか確認するだけなら、
- 複数クラスに同じように修正したけど、バラバラでコミットしちゃったりした時も、整理してからpushできるから、
git rebase -i
イイネ!