【git】開発とレビューのバランスを求めて 〜ブランチ分割編〜

開発時、どの粒度でcommitするか考えながらコードを書きたくない。
特に、開発中は試行錯誤しながら実装を進めているので、最初から設計しきれていない場合も多い。怠慢と言われてしまうと弱いが、開発スピードも考慮すると、試行錯誤するのが早い。
確かにそんな状態で開発を進めていると、commit履歴が汚れてしまうのも現実だ。このままレビュワーにレビュー依頼をすると、レビュワーの側の負担が大きくなってしまう1

要件の整理

以下ができると、あとはチームで運用ルールを定めればよさそうだ。

  • 後で、ブランチを分割したい
  • 後で、commitをまとめたい

  • (ついでに)masterが進んでしまった場合に、新しいHEADからbranchを分けたい2

これらを実現するため、試してみたやり方をメモとしてまとめる。

本記事では、「ブランチを分割したい」を検証する。

「commitをまとめたい」は以下の記事で別にした。

https://cipepser.hatenablog.com/entry/git-operation-merge-commitcipepser.hatenablog.com

前提

  • conflictするパターンは考えない3

やりたいこと

commitが積み重なり、変更が大きくなったブランチを考える。レビュワーの負担減を目的に、このブランチを分割したい。

やり方

git cherry-pickを使う

(準備)commit用のファイルを用意する

ls
README.md a.txt     b.txt

txtファイルの中身はファイル名が一文字書いてある。 例えばa.txtならaとだけ書いてある。適当に用意しただけなので、意味はない。

❯ cat a.txt
a

後続に、git diffしている箇所があるので、git logを補足(これが初期状態)。

❯ git log --pretty=oneline
635c5d5570a6c0492be7a924dda3df314bfd13b8 (HEAD -> master) first commit

大きくなりすぎたブランチを作る

大きくなりすぎた(a.txtとb.txtを同時に追加した)ブランチをadd-a-and-bブランチとする。

❯ git checkout -b add-a-and-b

a.txtを追加したcommitと、b.txtを追加したcommitを行う。
(「2つもcommitあるなんて大きすぎるよ」の状態)

❯ git add a.txt 
❯ git commit -m "add a.txt"

❯ git add b.txt
❯ git commit -m "add b.txt"

git cherry-pickでcommit idを使うので、git logを確認しておく。

❯ git log --pretty=oneline
451fcd8e75608ca6364fd2ad9f9699adf8a8b3fb (HEAD -> add-a-and-b) add b.txt
41295c0d3043f560a61f57a0f44d4785056b4269 add a.txt
635c5d5570a6c0492be7a924dda3df314bfd13b8 (master) first commit

(本題)ブランチを分割する

a.txtを追加するadd-aブランチと、b.txtを追加するadd-bブランチに分ける

add-aブランチ

masterブランチからadd-aブランチを作成する。

❯ git checkout master
❯ git checkout -b add-a

add a.txtのcommitをpickする。

# `first commit`から`add a.txt`のcommit idを指定
❯ git cherry-pick 635c5d5570a6c0492be7a924dda3df314bfd13b8..41295c0d3043f560a61f57a0f44d4785056b4269

結果(add-aのcommitだけpickできた)

❯ git log --pretty=oneline
b1abe76c7f481cecf64934a98dcd1971a13496e6 (HEAD -> add-a) add a.txt
635c5d5570a6c0492be7a924dda3df314bfd13b8 (master) first commit

add-bブランチ

masterブランチからadd-bブランチを作成する。

❯ git checkout master
❯ git checkout -b add-b
Switched to a new branch 'add-b'

add b.txtのcommitをpickする。

# `add b.txt`のcommi idを指定
❯ git cherry-pick 451fcd8e75608ca6364fd2ad9f9699adf8a8b3fb

結果(add-bのcommitだけpickできた)

❯ git log --pretty=oneline
bceca8ee93f256fb1d4083798c1aed1e3b1c8c16 (HEAD -> add-b) add b.txt
635c5d5570a6c0492be7a924dda3df314bfd13b8 (master) first commit

masterにマージする4

add-aブランチのマージ

masterブランチに戻る。

❯ git checkout master

masterブランチにadd-aブランチをマージする。実運用ではGitHub上でプルリクを送ることになると思う。

❯ git merge add-a
Updating 635c5d5..b1abe76
Fast-forward
a.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 a.txt

結果

❯ git log --pretty=oneline
b1abe76c7f481cecf64934a98dcd1971a13496e6 (HEAD -> master, add-a) add a.txt
635c5d5570a6c0492be7a924dda3df314bfd13b8 first commit

add-bブランチのマージ

こちらもgit mergeでマージする5

念のため、事前状態を記載しておく。

❯ git checkout add-b
Switched to branch 'add-b'

❯ git log --pretty=oneline
bceca8ee93f256fb1d4083798c1aed1e3b1c8c16 (HEAD -> add-b) add b.txt
635c5d5570a6c0492be7a924dda3df314bfd13b8 first commit

マージする。

❯ git checkout master
❯ git merge add-b
Merge made by the 'recursive' strategy.
b.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 b.txt

結果(add-aブランチとadd-bブランチを分割できている)

❯ git log --pretty=oneline
298b644f77788e7f2db45f21bfc56731fc46470d (HEAD -> master) Merge branch 'add-b'
bceca8ee93f256fb1d4083798c1aed1e3b1c8c16 (add-b) add b.txt
b1abe76c7f481cecf64934a98dcd1971a13496e6 (add-a) add a.txt
635c5d5570a6c0492be7a924dda3df314bfd13b8 first commit

「commitをまとめたい」については、以下に書いた。

https://cipepser.hatenablog.com/entry/git-operation-merge-commitcipepser.hatenablog.com


  1. 打ち消しcommitだったり、そもそもが意味のないcommitだったりも含まれてしまうことも…

  2. 自分でブランチを分割したときにも発生するので、実は対応が必須

  3. 今、考えたいこと以上に複雑な問題にしない)

  4. 実際には、マージ前にadd a.txtのレビューを行っているはず。

  5. add-aブランチをmasterにマージしたことで、add-bブランチから見るとmasterが先に進んでしまっている。git rebase masterだと、一番最初にbranchを分けた時点からのdiffになってしまい、レビュワーの負担が減らなかったため、git mergeを使っている。conflictするパターンも含めて深堀りが必要か。あくまで本記事の対象は、「レビュー負担軽減を目指し、conflictがない範囲でブランチ分割を行う」こととした。