あらまし
大きな作業をする場合、こまめにローカルレポジトリのブランチにコミットして、何かあったときにすぐに戻せるようにしたくなります。
また、パフォーマンス改善など、実験や研究の色合いの強い作業は、試行錯誤しながらブランチに"とりあえず"保存しつつ、「あっちのほうが良かったかな〜」と思ったときに取り出せるようにしておきたくなるものです。
また、ローカルレポジトリだけでなく、リモートレポジトリに置いたほうがチームみんなで共有できたりしていろいろ便利です。
ですが、最終成果物はなるべく少ないコミットにしないと、マージが大変です。
メインブランチにこんなコミットが入るとゲンナリしますよね?
$ git log --oneline
bcdef12 Revert foo
abcdef0 Add foo
cdef123 Refactor bar again
def1234 Refactor bar
ef12345 Implement baz
....
こんなかんじにスパっと決めてからレビューなりマージなりしたいものです。
特にマージは、コミットが無駄に多いとやたら衝突して手間はかかるやる気は下がる、いいことなし!!
$ git log --oneline
9876543 Refactor bar
210fedc Implement baz
....
なので、
- 手元では 作業を細かく分けてコミット しながら、
- ときに リモートブランチに保存 し、
- 最後に 成果物を細かくまとめる
ワークスタイルを紹介します。
※ リモートブランチはある程度自由にコミットしても良い、という前提で書いてます。
時間のない人用まとめ
以下、書いたはいいけど長ったらしいので短くまとめ。
git checkout -b foobar
git commit -m "hoge"
git commit -m "fuga"
git push origin HEAD:foobar-WIP # ローカルfoobar のまま、リモートのfoobar-WIPとしてpush
git commit -m "hoga"
git rebase -i FETCH_HEAD # コミット "hoge" "fuga" "hoga" をまとめられる
時間のある人用
まずは、メインブランチからブランチを切り、作業を進めます。
$ git checkout master -b feature-1234
$ git branch
* feature-1234
master
$ git push origin feature-1234
# stuff
$ git commit -m "Implement baz"
# stuff
$ git commit -m "Refactor bar"
リモートへの一時的な作業状態の保存
このあたりで、「ちょっと手戻りするかも...」と思ったら、 以下の方法でリモートリポジトリに保存 します。
$ git push origin HEAD:feature-1234-WIP # Work In Progress
$ git branch
* feature-1234
master
HEAD:remote-branch
とするのがミソです 。
これをすることで、 ローカルレポジトリのブランチをfeature-1234
から変えずに、リモートレポジトリのfeature-1234-WIP
として保存できます 。
また、feature-1234
としてgit push
しないことで、あとで作業成果をまとめるときに楽できます。
ローカルの変更をまとめる
作業を進めましょう。
$ git commit -m "Refactor bar again"
# stuff
$ git commit -m "Add foo"
# stuff
$ git commit -m "Revert foo"
$ git log --oneline
bcdef12 Revert foo
abcdef0 Add foo
cdef123 Refactor bar again
def1234 Refactor bar
ef12345 Implement baz
...
いいところまで実装できたので、 git rebase -i
で作業成果をまとめましょう。
どのハッシュを指定するか 悩む必要はありません。 何も考えずに FETCH_HEAD
を指定しましょう。
FETCH_HEAD
は、最後にリモートレポジトリにgit push
した変更を示しています 。
FETCH_HEAD
から始めればgit push
するときに Fast-Forwardできないエラーに遭遇せず 、かつ まとめることのできる変更すべてを列挙することができます 。
$ git rebase -i FETCH_HEAD
このコマンドで、最後にリモートブランチと同期した直後からのコミットがエディタに出てきます。
pick 461a05e Implement baz
pick 439994c Refactor bar
pick 3e5b24b Refactor bar again
pick 314c11c Add foo
pick 9aa0335 Revert foo
# Rebase 3d7e901..9aa0335 onto 3d7e901
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
さて、あとは毎回丁寧に書いてある下の説明書き通りにやりましょう
pick 461a05e Implement baz # そのまま
pick 439994c Refactor bar # そのまま
+fixup 3e5b24b Refactor bar again # fixup で前の変更とくっつける
-pick 3e5b24b Refactor bar again
-pick 314c11c Add foo # いらないので捨てる
-pick 9aa0335 Revert foo # 同じく
ファイルを保存すると書いたとおりに各変更が処理されていきます。
ログを見て意図通りにまとめられているのを確認したら、成果物としてブランチにgit push
します。
$ git log --oneline
98d701e Refactor bar
461a05e Implement baz
...
$ git push origin feature-1234
おしまい。
参考など
以前、自身で書いた記事。
FETCH_HEAD
について
Help me!
記事を書いているときに気づいたんですが、
現在のブランチ だけ を表示してくれる方法ってなんかないですかね。
文中ではgit branch
使ってるんですけどしっくりこない。
なにかいいのがあったらコメントください。
あと間違いあったら遠慮無く編集リクエストください。