gitで誤ったブランチに対して行った変更を正しいブランチに移す(stash編)


2011年 01月 10日

問題

gitで誤ったブランチに対して行った変更を正しいブランチへ移す(cherry-pick編)
では一度コミットしてしまった変更を別のブランチへ付け変える方法について紹介しました。

この例では誤ったブランチに対して一度コミットしてしまいましたが、
コミットする前に誤ったブランチで作業していたことに気付くこともよくあります。
例えば以下のような状態です:

$ git branch
  master
* topic-y

$ git branch topic-x master

$ $EDITOR  # git checkout topic-x を忘れて作業してしまった。

$ git status
# On branch topic-y
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   application.source
#       modified:   library.source
#
no changes added to commit (use "git add" and/or "git commit -a")

一度コミットしていたのであればgit cherry-pickで対処できますが、
この場合はまだコミットしていません。
どうすればよいでしょうか。

解決方法

git stashを使います。具体的には以下の通りです:

$ git status  # (1)
# On branch topic-y
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   application.source
#       modified:   library.source
#
no changes added to commit (use "git add" and/or "git commit -a")

$ git stash save
Saved working directory and index state WIP on topic-y: 0100002 Update to use Y
HEAD is now at 0100002 Update to use Y

$ git status  # (2)
# On branch master
nothing to commit (working directory clean)

$ git checkout topic-x
Switched to branch 'topic-x'

$ git stash pop  # (3)
# On branch topic-x
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   application.source
#       modified:   library.source
#
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (fffffff000000000000000000000000000000000)

解説

git stash save を実行すると
まだコミットされていない変更を一旦脇に置いて
作業ディレクトリの内容を何の変更もないクリーンな状態に戻します。
また、git stash pop を実行すると
先程脇に置いた変更を適用し直すことができます。

これを利用すれば
「まだコミットしていない変更を一旦脇に置いておいて、
正しいブランチに切り替えた後で脇に置いた変更を元に戻す」
という形で
誤ったブランチに対して行った変更を正しいブランチへ移すことができます。

なお、
git stash save で「脇に置いた」変更はスタックのように積まれていきます。
git stash pop では最後に積まれた変更を適用してスタックから取り除いています。
このスタックに積まれている変更は git stash list で確認することができます。
より詳しくはgit stash のマニュアルを参照してください。

補足

やろうと思えば以下のコマンドで git stash と同様のことができます。
ただ面倒なので普通は git stash を使います。

$ git branch
  master
  topic-x
* topic-y

$ git status
# On branch topic-y
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   application.source
#       modified:   library.source
#
no changes added to commit (use "git add" and/or "git commit -a")

$ git checkout -b tmp
M       application.source
M       library.source
Switched to branch 'tmp'

$ git commit -am 'WIP'

$ git checkout topic-x
Switched to branch 'topic-x'

$ git cherry-pick -n tmp
Finished one cherry-pick.

$ git branch -D tmp
Deleted branch tmp (was fffffff).

$ git status
# On branch topic-x
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   application.source
#       modified:   library.source
#