[Git]誤ったブランチで実施した変更を正しいブランチに移動する
こんにちは、CX事業本部の若槻です。
Git環境での開発作業で、本来はFeatureブランチで作業するべきところを誤ってMasterブランチで作業してしまったということが時々あります。そのような時に誤ったブランチから本来の正しいブランチに変更を移動する方法を確認してみました。
正しいブランチに変更を移動する方法
移動したい変更がどういう状態なのかによって対処方法が変わってきます。今回は以下の方法について確認してみます。
- 移動したい変更が誤ったブランチ(
master
)の作業ツリーにある場合 - 移動したい変更が誤ったブランチ(
master
)のステージにある場合 - 移動したい変更が誤ったブランチ(
master
)でコミット済みの場合- 正しいブランチ(
feature
)を作成済みの場合 - 正しいブランチ(
feature
)を未作成の場合
- 正しいブランチ(
移動したい変更が誤ったブランチ(master)の作業ツリーにある場合
誤ってmaster
ブランチ上で作業をしてしまったことに変更のステージング前に気付いた、という場合の対処方法です。
git status
するとChanges not staged for commit
欄(ファイル新規作成の場合はUntracked files
欄)にファイルの変更が登録されており、変更が作業ツリーにあることが分かります。
$ git status On branch master Your branch is up-to-date with 'origin/master'. 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: Makefile no changes added to commit (use "git add" and/or "git commit -a")
git stash save
でmaster
ブランチで作業ツリー上の変更を退避します。(特定の変更のみ退避したい場合はgit stash save -p
)
$ git stash save Saved working directory and index state WIP on master: <HEADのコミット情報>
git stash list
で変更がstash上に登録されていることを確認します。
$ git stash list stash@{0}: WIP on master: <HEADのコミット情報>
git status
するとmaster
ブランチの作業ツリーから変更が消えていることが分かります。
$ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working tree clean
変更の移動先となるfeature
ブランチをチェックアウトします。
$ git checkout feature
退避した変更をgit stash pop
でfeature
ブランチに適用(同時にstashから削除)します。変更がfeature
ブランチの作業ツリーに登録されます。
$ git stash pop stash@{0} On branch feature 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: Makefile no changes added to commit (use "git add" and/or "git commit -a") Dropped stash@{0} (e31990220a03acf34021a3dc2d8ae4f934dd84af) ```nges added to commit (use "git add" and/or "git commit -a")
これでmaster
ブランチからfeature
ブランチに作業ツリー上の変更を移動できました。
移動したい変更が誤ったブランチ(master)のステージにある場合
誤ってmaster
ブランチ上で作業をしてしまったことに変更のステージング後に気付いた、という場合の対処方法です。
git status
するとChanges to be committed
欄にファイルの変更が登録されており、変更がステージにあることが分かります。
$ git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: Makefile
まずはgit reset
でmaster
ブランチのステージ上の変更をHEADの状態に戻します。(特定のファイルの変更のみ戻したい場合はgit reset HEAD -- [ファイル名]
)
$ git reset HEAD Unstaged changes after reset: M Makefile
git status
すると、移動したい変更が作業ツリーにある状態に戻ったことが分かります。
$ git status On branch master Your branch is up-to-date with 'origin/master'. 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: Makefile no changes added to commit (use "git add" and/or "git commit -a")
ここからの対応は変更が作業ツリーにある場合の対応と同じです。
git stash save
でmaster
ブランチで作業ツリー上の変更を退避します。(特定の変更のみ退避したい場合はgit stash save -p
)
$ git stash save Saved working directory and index state WIP on master: <HEADのコミット情報>
git stash list
で変更がstash上に登録されていることを確認します。
$ git stash list stash@{0}: WIP on master: <HEADのコミット情報>
git status
するとmaster
ブランチの作業ツリーから変更が消えていることが分かります。
$ git status On branch master Your branch is up-to-date with 'origin/master'. nothing to commit, working tree clean
変更の移動先となるfeature
ブランチをチェックアウトします。
$ git checkout feature
退避した変更をgit stash pop
でfeature
ブランチに適用(同時にstashから削除)します。変更がfeature
ブランチの作業ツリーに登録されます。
$ git stash pop stash@{0} On branch feature 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: Makefile no changes added to commit (use "git add" and/or "git commit -a") Dropped stash@{0} (e31990220a03acf34021a3dc2d8ae4f934dd84af)
これでmaster
ブランチからfeature
ブランチにステージ上の変更を移動できました。
移動したい変更が誤ったブランチ(master)でコミット済みの場合
誤ってmaster
ブランチ上で作業をしてしまったことにコミット後に気付いた、という場合の対処方法です。
master
ブランチ上のm00011
からm00013
までの3コミットがfeature
ブランチに移動したい変更だとします。
$ git log --oneline master m00013 (HEAD -> master) 移動したい変更3 m00012 移動したい変更2 m00011 移動したい変更1 m00010 (origin/master, origin/HEAD)
正しいブランチ(feature)を作成済みの場合
変更の移動先となるfeature
ブランチが既に作成されている場合は、master
からfeature
に変更をマージした上で、master
ブランチで変更をリセットすることにより対処可能です。(feature
とmaster
の間で競合する変更が行われた場合など、例外はあります。)
まず変更の移動先となるfeature
ブランチをチェックアウトします。
$ git checkout feature
master
ブランチ上のコミットをfeature
ブランチにマージします。
$ git merge master Updating f00010..m00013 Fast-forward Makefile | 3 +++ 1 file changed, 3 insertions(+)
git log
するとmaster
ブランチ上でコミットした変更がfeature
ブランチに移動されていることが分かります。
$ git log --oneline m00013 (HEAD -> feature, master) 移動したい変更3 m00012 移動したい変更2 m00011 移動したい変更1 f00010 featureの元HEADの変更
次にmaster
ブランチをチェックアウトします。
$ git checkout master
git reset --hard
でmaster
ブランチから移動済みのコミットを削除します。
$ git reset --hard HEAD~3 HEAD is now at m00010
git log
すると移動した変更のコミットがmaster
ブランチから削除されていることが分かります。
$ git log --oneline m00010 (HEAD -> master, origin/master, origin/HEAD) m00009 m00008 m00007
これでmaster
ブランチからfeature
ブランチ(作成済み)に変更のコミットを移動できました。
正しいブランチ(feature)が未作成の場合
変更の移動先となるfeature
ブランチがまだ作成されていない場合は、feature
ブランチを作成した上でmaster
ブランチで変更をリセットするのみで対処可能です。
変更の移動先となるfeature
ブランチを作成します。
$ git checkout -b feature
master
ブランチをチェックアウトします。
$ git checkout master
git reset --hard
でmaster
ブランチから移動したい変更のコミットを削除します。
$ git reset --hard HEAD~3 HEAD is now at m00010
git log
すると移動したい変更のコミットがmaster
ブランチから削除(HEADの位置がリセット)され、feature
ブランチには残っていることが分かります。
$ git log --oneline --decorate master feature m00013 (feature) 移動したい変更3 m00012 移動したい変更2 m00011 移動したい変更1 m00010 (HEAD -> master, origin/master, origin/HEAD)
これでmaster
ブランチからfeature
ブランチ(未作成)に変更のコミットを移動できました。
おわりに
誤ったブランチで作業してしまい対処に焦るということを良くやるので、わたし含めた初心者の人ほど転ばず先の杖としてリカバリ方法を心得ておくと安心ですね。