あるコミットで致命的なバグが混入した時に、そのコミットの前まで状態を巻き戻したい場合があります。
ここではその方法について学びます。


(1) HEADの状態までワーキングツリーとインデックスの内容全てを一気にリセット

まず HEAD の状態までワーキングツリーとインデックスの内容全てを一気にリセットする方法について学びます。
なお個別のファイルをリセットしたい場合は7ページ目で学んだ git restore --staged コマンドと git restore コマンドを使った方が安全です。

HEADの状態までワーキングツリーとインデックスの内容全てを一気にリセット:
git reset --hard HEAD

※ 更新内容が完全に全て消えるので要注意

試しに状態 18に対して上のコマンドを実行すると次の状態19になります。
ワーキングツリーとインデックスがHEADの状態まで戻ります。


状態 19


ワーキングツリー
hoge.txt
ab


インデックス
hoge.txt
ab


ローカルリポジトリ「gitlocal」
HEAD
hoge.txt
ab
"piyo.txtを削除(S16)"
HEAD~
hoge.txt
ab
piyo.txt
AB
"piyo.txtを更新(S9)"
HEAD~2
hoge.txt
ab
piyo.txt
A
"hoge.txtを更新(S8)"
HEAD~3
hoge.txt
a
piyo.txt
A
"piyo.txtを追加(S5)"
HEAD~4
hoge.txt
a
"hoge.txtを追加(S4)"

(2) 安全にコミットを巻き戻す

次に安全にコミットを巻き戻す方法について学びます。

N 個前のコミットまで安全にコミットを巻き戻す方法:
 (1) revert コマンドを N 回繰り返す

git revert -n HEAD
git revert -n HEAD~1
git revert -n HEAD~2
:
:
git revert -n HEAD~(N-1)

※ 1 回だけ巻き戻したい時は git revert -n HEAD だけで結構です。
※ -n オプションを付けないと同時にコミットが実行されてしまうので気をつけましょう。

 (2) diff コマンドで差分が無いか調べる

git diff HEAD~N

 (3) コミットする

git commit -m "N回巻き戻し"

「巻き戻し」がどういう動作なのかピンと来ない人がいると思いますので、実際に演習してみましょう。

状態19から N = 3 回巻き戻し、"3回巻き戻し(S20)" というコメントでコミットしてください。
すると以下の状態20となります。
状態20のワーキングツリー、インデックス、HEADの内容が状態19のHEAD~3( = 状態20のHEAD~4)と同じ内容になっていることに注目して下さい。

ただし、過去のコミットは残っていますので、もし巻き戻した結果が気に入らなければまた revert を使っていつでも前の状態に戻れます。


状態 20


ワーキングツリー
hoge.txt
a
piyo.txt
A


インデックス
hoge.txt
a
piyo.txt
A


ローカルリポジトリ「gitlocal」
HEAD
hoge.txt
a
piyo.txt
A
"3回巻き戻し(S20)"
HEAD~
hoge.txt
ab
"piyo.txtを削除(S16)"
HEAD~2
hoge.txt
ab
piyo.txt
AB
"piyo.txtを更新(S9)"
HEAD~3
hoge.txt
ab
piyo.txt
A
"hoge.txtを更新(S8)"
HEAD~4
hoge.txt
a
piyo.txt
A
"piyo.txtを追加(S5)"
HEAD~5
hoge.txt
a
"hoge.txtを追加(S4)"


(参考) 過去のコミットを消す

参考までに過去のコミットを消して無かったことにする方法について学びます。
簡単ですが危険ですのでなるべく使わない方が良いです。
特にリモートリポジトリを使って多人数でチーム開発する時は、他のメンバーに大迷惑がかかるのでこの方法は厳禁です。

N 回分のコミットを消す方法:
git reset --hard HEAD~N

※ コミットが消えるので要注意

例えば状態 20 に対して

git reset --hard HEAD~3

を実行して過去 3 回分のコミットを削除すると HEAD 、HEAD~1、HEAD~2 が消え、状態が状態 8 まで戻ります。