git rebaseでコミットをきれいにする方法です。
使うタイミング
自分専用のトピックブランチで作業が完了して 元のブランチに戻す前 に行います。
練習用のリポジトリ作成
sample
というファイルに機能が3つインプリされているという想定のテスト用のリポジトリを作成します。
$ git init test $ cd test $ for i in `seq 1 3`; do printf "func%d\n\n" $i; done > sample $ cat sample func1 func2 func3 $ git add sample; git commit -m "init" [master (root-commit) c509fc6] init 1 file changed, 6 insertions(+) create mode 100644 sample $
トピックブランチを作って、3つの機能の全てに修正を加えてコミットしたと想定します。
$ git checkout -b topic Switched to a new branch 'topic' $ sed -ie 's/\(func.\)/\1+fix/' sample $ cat sample func1+fix func2+fix func3+fix $ git commit -a -m "fix" [topic 77adf00] fix 1 file changed, 3 insertions(+), 3 deletions(-) $
この時点で、気がつけば、git reset HEAD
で戻すことも可能ですが、気が付いたときには、もう一つ機能が追加されていたとします。
$ echo func4 >> sample $ git commit -am "これは、正しいコミットで修正したくない" [topic e836500] これは、正しいコミットで修正したくない 1 file changed, 1 insertion(+) $
テスト用のリポジトリは、こんな感じになります。
$ git --no-pager log --pretty=oneline e836500e25943c80e67fcbfdcfef10e0d8fc0040 これは、正しいコミットで修正したくない 77adf00234c23ceda93be668eef460a218944f5a fix c509fc62f1a14a5d374c20203fecec670cf82a6f init $
コミットを分割する
3つの機能の修正は、独立しているので、修正毎に3つのコミットにするべきだったと想定して、コミットを分割します。
対象のコミットを確認する。
git show-branch
コマンドで、現在のtopicブランチに2つのコミットがありtopic^
に修正したいコミットがあることがわかります。
$ git show-branch ! [master] init * [topic] これは、正しいコミットで修正したくない -- * [topic] これは、正しいコミットで修正したくない * [topic^] fix +* [master] init $
対象のコミットを修正
git rebase -i
を使って対象のコミットを修正します。
対象は、topic^
なので、その一つ前のmaster
を指定します。
$ git rebase -i master
エディタが開いて対象のコミットが下記のように表示されます。
pickとなっている部分で[e]
を選択してエディタを押します。
pick 77adf00 fix pick e836500 これは、正しいコミットで修正したくない # Rebase c509fc6..e836500 onto c509fc6 (2 command(s))
修正したいコミットのpick
がedit
に変わったのを確認してエディタを修正します。
edit 77adf00 fix pick e836500 これは、正しいコミットで修正したくない # Rebase c509fc6..e836500 onto c509fc6 (2 command(s))
エディタが終了すると、こんなメッセージが表示されます。
Stopped at 77adf00234c23ceda93be668eef460a218944f5a... fix You can amend the commit now, with git commit --amend Once you are satisfied with your changes, run git rebase --continue $
git reset
で修正を戻します。
$ git reset HEAD^ Unstaged changes after reset: M sample $
修正内容を分割
git add -p
を使って修正を分割します。
下記のように表示されるので、必要な修正まで[s]
で分割していきます。
$ git add -p diff --git a/sample b/sample index bd073e0..b9dc832 100644 --- a/sample +++ b/sample @@ -1,6 +1,6 @@ -func1 +func1+fix -func2 +func2+fix -func3 +func3+fix Stage this hunk [y,n,q,a,d,/,s,e,?]?
[s]
を押すと次のように、コミットが分割されます。
Stage this hunk [y,n,q,a,d,/,s,e,?]? s Split into 3 hunks. @@ -1,2 +1,2 @@ -func1 +func1+fix Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]?
[y]
を押して、分割されたコミットがステージされた状態にします。
[q]
を押して一旦、終了します。
Stage this hunk [y,n,q,a,d,/,j,J,g,e,?]? y @@ -2,3 +2,3 @@ -func2 +func2+fix Stage this hunk [y,n,q,a,d,/,K,j,J,g,e,?]? q $
git status
で確認すると下記のようになります。
$ git status interactive rebase in progress; onto c509fc6 Last command done (1 command done): edit 77adf00 fix Next command to do (1 remaining command): pick e836500 これは、正しいコミットで修正したくない (use "git rebase --edit-todo" to view and edit) You are currently splitting a commit while rebasing branch 'topic' on 'c509fc6'. (Once your working directory is clean, run "git rebase --continue") Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: sample Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git checkout -- <file>..." to discard changes in working directory) modified: sample $
下記のように分割したコミットだけがステージされるので、これを、あと2回繰り返して、3つのコミットに分割します。
$ git --no-pager diff --staged diff --git a/sample b/sample index bd073e0..604342d 100644 --- a/sample +++ b/sample @@ -1,4 +1,4 @@ -func1 +func1+fix func2 $ git --no-pager diff diff --git a/sample b/sample index 604342d..b9dc832 100644 --- a/sample +++ b/sample @@ -1,6 +1,6 @@ func1+fix -func2 +func2+fix -func3 +func3+fix $
ログが分割されたことを確認します。
$ git --no-pager log --pretty=oneline 04d8da4ce4057583d6b917f0b1e481211fdb207c func3 e780e2cbd878d6d7bc2c9dd99cb1e0cefc9131dc func2 8263453e933119f5397b96267425c357731120e0 func1 c509fc62f1a14a5d374c20203fecec670cf82a6f init $
git rebase --continue
でログの修正を終了します。
$ git rebase --continue Successfully rebased and updated refs/heads/topic. $
これで、コミットが分割されました。
$ git show-branch ! [master] init * [topic] これは、正しいコミットで修正したくない -- * [topic] これは、正しいコミットで修正したくない * [topic^] func3 * [topic~2] func2 * [topic~3] func1 +* [master] init $
以上