gitでのヤバイ！を取り消す方法

gitでよくある、やってしまった！を取り消す方法を紹介します。

gitを使っていると、「ヤバイ・・間違えてcommitしてしまった」「消しちゃいけないものを`git reset --hard`しちゃった」とか色々なやばいがあります。
そのヤバイを取り消す便利な方法を備忘録として記録しておきます。

### 【case1】 commit内容が間違っていた。取り消して再度commitしたい

直前のcommitだけであれば、`git commit --amend`を使えば解決出来ます。

1. ファイルに修正を加えて、commit
2. 間違っていた事に気づいたので、更に修正を加えた
3. `git add`して`git commit --amend`

これでOKです。

### 【case2】過去のcommitが誤っていた。commit自体を取り消したい

よくあるようなパターン(私はやってしましますｗ)として、ローカルで作業してる時、
何か修正を加えたが、それを誤って全て`master`ブランチにcommitしていたとします。

そこで、masterでのcommitをなかった事にして、`bug_fix`ブランチでcommitしてた事にします。

実践してみます。

#### 間違ってmasterにcommitしてしまった。。。

新しく、何かを登録する機能と編集する機能を作ってコミットした。
でも今いるブランチを見たら`master`だった。。。
今回は、`538682b`〜`ac27fb2`までを別のブランチ(`register_edit`)で作業したかっとたします。

```bash
$ git log --oneline
ac27fb2 編集処理を追加
040172e 登録処理を修正した
538682b 登録処理を追加した
129d634 ひな形を用意

$ git br
* master
```

#### 本来作るはずだったブランチから分岐させる

ローカルのmasterにはすでに誤ったコミットがされちゃっているので、
リモートのmasterから分岐させてブランチを作ります。

```bash
$ git br register_edit origin/master
$ git ch register_edit

$ git br
  master
* register_edit
```

ついでにこの作ったブランチに移動しときます。

#### 誤ってmasterにしちゃったcommitをコピーする

```bash
$ git cherry-pick 538682b
$ git cherry-pick 040172e
$ git cherry-pick ac27fb2

$ git log --oneline
1d1bb07 編集処理を追加
83528f9 登録処理を修正した
bacaf7e 登録処理を追加した
```

↑ 新しいブランチにcommitがコピー出来ました。
後はmasterでやってしまった誤ったcommitを消せれば完了です。

#### 誤ってたコミットを取り消す

```bash

# masterブランチに戻る
$ git ch master
Switched to branch 'master'

# このcommitのうち、3つを消したい → 129d634まで戻りたい
$ git log --oneline
ac27fb2 編集処理を追加
040172e 登録処理を修正した
538682b 登録処理を追加した
129d634 ひな形を用意

# コミットを取り消す
$ git reset --hard 129d634
HEAD is now at 129d634 ひな形を用意  # HEADの位置が変わりました

# 消したかったcommitが消えてます
$ git log --oneline
129d634 ひな形を用意
d0cab23 create test1.txt
```

これでmasterにやった間違ったcommitを別ブランチに移して、
masterのcommitを消すことが出来ました。
めでたしめでたし

#### 【番外編】 `git reset --hard`を取り消したい。

上のようにコミットログを書き換える場合、`git reset --hard`する事があるかと思います。
でも、`git reset --hard`のコミット番号が間違っていた。。
あぁあああもう終わりだ。。
修正が全部消えてしまった。。
という絶望感に襲われますが、gitなら大丈夫。
`git reset`した内容も取り消す事が出来ます。

実際に見てみましょう。

```bash

# 適当に2つコミットをしてたのが間違ってたから消したい → 129d634に戻りたい
$ git log --oneline
7938107 適当なcommit2
12f8a66 適当なcommit1
129d634 ひな形を用意
d0cab23 create test1.txt

# 間違ってd0cab23まで戻して、消すはずじゃなかった129d634も消してる
$ git reset --hard d0cab23
HEAD is now at d0cab23 create test1.txt

# 129d634も消してしまった。。。。
$ git log --oneline
d0cab23 create test1.txt
```

でも大丈夫。gitはresetした内容を`reflog`に残しています。
そして、`git reset`の取り消しも`git reset`でできます。

```
# reflogにはgit resetの内容が残っている。
$ git reflog -n 4
d0cab23 HEAD@{0}: d0cab23: updating HEAD
7938107 HEAD@{1}: commit: 適当なcommit2
12f8a66 HEAD@{2}: commit: 適当なcommit1
129d634 HEAD@{3}: 129d634: updating HEAD

# git resetする前まで戻る
$ git reset --hard 7938107
HEAD is now at 7938107 適当なcommit2

# コミットログが戻ってきた！
$ git log --oneline
7938107 適当なcommit2
12f8a66 適当なcommit1
129d634 ひな形を用意
d0cab23 create test1.txt

# 改めて正しい所までgit resetしなおす
$ git reset --hard 129d634
HEAD is now at 129d634 ひな形を用意

# 今回は正しく出来ました
$ git log --oneline
129d634 ひな形を用意
d0cab23 create test1.txt
```

`git reset`とか、よく分かっていないと怖いですが、
戻せるという事がわかってると安心して使えます。

### 【case3】誤ったcommitをリモートにPUSHしちゃった

先ほどのように、適当なcommitをしたまま、リモートブランチにもマージしちゃった場合はどうしたらいいのか。

```bash
# 適当なcommi1と適当なcommit2は後から取り消したくなる
$ git log --oneline
7938107 適当なcommit2
12f8a66 適当なcommit1
129d634 ひな形を用意
d0cab23 create test1.txt

# 間違っているのにそのままPUSHしちゃった
$ git push origin hoge
Password: 
Total 0 (delta 0), reused 0 (delta 0)
To **************　                     # ← 意図的に伏せてます
 * [new branch]      hoge -> hoge

# 本来であれば、ここでリモートブランチ(origin/hoge)を消して、
# コミットを修正した内容をPUSHすべきですが、パニックになってて、とりあえずコミットログを修正して、PUSHを試みた場合を想定してみます。

# 誤っていたコミットログを消す
$ git reset --hard 129d634
HEAD is now at 129d634 ひな形を用意

# よし、消えてる。
$ git log --oneline
129d634 ひな形を用意
d0cab23 create test1.txt

# さぁ、Pushし直すぞ
$ git push origin hoge
Password: 

# 歴史が変わっているのでPUSHが拒否されます
To ************
 ! [rejected]        hoge -> hoge (non-fast-forward)
error: failed to push some refs to '***********'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again.  See the 'Note about
fast-forwards' section of 'git push --help' for details.

# ここでリモートブランチを削除
$ git push origin :hoge
Password: 
To *************
 - [deleted]         hoge

# 再度PUSHする！
$ git push origin hoge
Password: 
Total 0 (delta 0), reused 0 (delta 0)
To ***************
 * [new branch]      hoge -> hoge
```

これで誤ったコミットをリモートにPUSHしてた場合でも解決出来る。

【case 4】コンフリクト解消作業を中止する

マージして、コンフリクトが発生し、解消しようと努力したが、こんがらがって中止したくなった場合の対処法です。

```
git reset --hard ORIG_HEAD
```