本題に入る前に前提知識を確認したい。gitのログである。
logとreflog
gitには2つのログ機構がある。logとreflogである。
logはHEADの変遷、reflogはcommit履歴である。すなわちlogとreflogの違いとは、logは「公式な記録」、reflogは「ローカルの非公式な記録」だと考えてよいだろう。gitで戻れることができるのは、基本的にはこの2つで見れるものである。
ダメ・ゼッタイ git reset HEAD --hard
さて本題である。「やってみたけど全然違った」ときに、書いたコードを消して以前の状態に戻りたい、というケースではどのような操作をすべきだろうか。
一番簡単な解決策は、git reset HEAD --hardすることである。これでHEAD(すなわち最新のコミット)に戻ることができる。
しかし、この解決策には問題がある。上記で述べたとおり、commitはされていないものは記録がされておらず、その時点には戻ることは決してできなくなってしまうという問題だ。
「全てではないけど、一部のコードが有用だった」というケースでもう一度書き直してしまうのは、深い悲しみがある。たとえその時点で必要がなさそうであっても、あとで再利用したくなるという状況は頻繁に起こりえるだろう。
解決策
一番大切なことは、「commitしていないものには戻ることができない」という事実である。これは逆に言えば、「commitしとけばなんとかなる」ということである。
よって、ベターな方法は
(1) git add -A
(2) git commit -m "save it just in case"
(3) git reset HEAD~1 --hard
することだろう。
(1)では全く新しいファイルも含めてインデックスに乗せている。
(2)ではコミットし、(3)で今したばかりのコミットを、HEAD、インデックス、ワーキングディレクトリの全てから完全に消している。
利点
こうすることで、いざ必要になった時に、git reflogからこのcommitを辿ることができる。HEADが(2)の時点で一度このコミットを指すからである。
またその一方、(3)でHEADを一つ前のcommitに戻すことによって、git log(正式な記録)から削除し、これまでの変更もなかったことになる。
こうしたステップを踏むことで、安心して変更を取り消すことができる。
git alias
(1)~(3)までのコマンドは毎回打つのはだるいし、エイリアスにしておくとよいと思う。
save-reset = !git add -A && git commit -qm 'SAVE RESET' && git reset HEAD~1 --hard
これを.gitconfigの[alias]下に書いておくと幸せになれるんじゃないかな多分。