【Git】コミットの取り消し方をおさらいしよう！【初心者】

# 概要
### この記事で扱うこと
  - Gitの基本的なコマンドである```git reset```や```git revert```の扱い方
  - ```git reset```、```git revert```のイメージ
### この記事を読むべき人
  - Git初心者
  - 「addとかcommitとかはわかるけど実践で使ってみたらなんかよくわからん！」という方

:::note warn
「Gitって？」「リモートリポジトリって何？」といった基本の「き」的な内容は取り扱いません🙇
:::

# 初めに
WHIのアドベントカレンダ―本日分を担当させていただきます！
```git add```, ```git commit```, ```git push```など、リモートリポジトリへ反映する方法はなんとなく知っていても、それを取り消す方法って意外にまとまってないですよね…
自分も新卒エンジニアという身なので、改めてGitの使い方をおさらいしようと思います！

# git reset, revert の基本的な使い方
というわけで、まずは基本的な使い方からまとめていきます。


### git reset
```bash
$ git reset --soft HEAD^  //直近のcommit等を取り消す
```
- 直前の操作を取り消します。（commitやpushなど）
- 第１引数では**どのレベルで巻き戻すか**を指定します。
  - ```--soft``` : 直前の操作のみを取り消し、編集をステージング(```git add```)した状態に戻す
  - ```--mixed``` : 直前の操作とステージングを取り消し、編集したばかりの状態に戻す
  - ```--hard``` : 直前の操作・編集を丸ごとなかったことにする（**※使用注意！**）


- 第２引数では**どのコミットまで戻すか**を指定します。
  - ```HEAD^``` : 直前の操作を元に戻す
  - ```HEAD^^``` : 直近2回分の操作を元に戻す（以下、```^```の数で戻す回数を指定）
  - コミットIDの指定 : 指定したIDのコミットまでの操作を元に戻す
    - コミットIDは```git log```コマンドやGithubのコミット履歴（※）で確認可能
    ※ 画像赤枠部
    ![スクリーンショット 2023-11-27 234621.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3515840/81a101a6-2957-509e-eb6c-97fb33a1bc9a.jpeg)



### git revert
```bash
$ git revert コミットID 
```
- **指定したのコミットを打ち消すコミットを作成**します。例えば「Aという文字を書き加える」というコミットを対象にrevertを実行すると、「Aという文字を削除する」というコミットが作成されます。

- 引数では**打消しのコミットの対象**を指定します。
  - コミットIDの指定 : 上述の```git reset```の参照方法と同じ
- コミット時と同様、コマンドを実行するとコミットメッセージの編集画面に遷移します。任意のメッセージを入力し、```:wq```で保存＆quitしてください。
- 「逆向きの変更は作りたいが、一度にコミットまでしたくない」という場合は```git revert -n コミットID```のように[-n]コマンドが使えます。


# 何が違う？
両者の基本的な使用法はおさらいしました。ですが、どのような差があり、どう使い分ければいいのでしょうか？

結論からいうと、両者の差は**コミット履歴が残るかどうか**という点だけです。
ここでは両者のイメージをまとめておきます。

### git reset
resetはコミットそのものをなかったことにし、今いる地点を正とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3515840/cf12804f-f639-c419-fe01-d05df4cedc1b.png)
過去を改変したことによってその先の未来がなくなってしまうイメージです。~~その手のアニメだとあるあるですね！~~
コミット履歴まで消えてしまうため、ユースケースとしては
- 「本当に間違ってコミットしてしまった」
- 「コミット後に誤りに気付いたが軽度なので誰かに見られる前に直しちゃいたい」
などが考えられそうです。
コミット履歴欄に余計なものが残らないので、ごちゃつかずに済みますね！

##### 起こりうるエラーと解決法
ただし、歴史の改変は往々にして世界線の不整合を生み出してしまいます…
**resetをしたのちに```git push```をしようとするとエラーが表示されてしまう**のです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3515840/61cc200c-48ae-07fa-9621-b47ad9e6f6b8.png)
上図では、要所要所でリモートリポジトリに変更を反映している状態で、ローカルでcommit3をresetし、その先の変更を反映しようとしています。
ですが、リモートリポジトリからすると「2の次に3があって、その次に4」という歴史を経ているので、急にcommit4'（ダッシュ）が来ても「歴史が違うのでプッシュされても困る！（どちらの歴史が正しいかわからない！）」となってしまうのです。

この状態を解決する方法は以下2つがあげられます。
**1. ```git fetch```などでローカルの状態をリモートリポジトリに合わせる**
**1. ```git push origin head -f```(強制プッシュ)を行うことで、リモートリポジトリの状態をローカルに合わせる**

いずれもリモートと手元のソースをそろえるという手間がかかります。強制プッシュを行ってしまえばエラーなくpushが可能ですが、共同で編集しているブランチであれば、メンバーの方に一声かけるのが賢明でしょう。



### git revert
revertは逆向きのコミットを生成するので、あくまで時系列としては先に進んでいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3515840/9d7dbbbd-fbdb-98eb-eae9-c52c1fcf17b6.png)

resetが「コミットなんて無い！0に戻す！ **（~~1~~ ⇒ 0）**」なのに対し、
revertは「コミットが+1されたので、-1のコミットを足して0に戻します～ **( 1 + (-1) = 0 )** 」というイメージです。

過去改変を行うわけではないので、reset後のpush時に起こるようなエラーは起こりえません。
また、修正したことが明示的になるので、「この人は意図的に修正を入れたんだな・コミットを取り消そうとしたんだな」ということが他の人からもわかります。

ただし、あくまで **「逆向きのコミットを生成する」** コマンドなので、コミット履歴が残ってしまう点は注意が必要です。

# まとめ
お疲れ様でした！
今回はGitにおける取り消し手段の代名詞である```git reset```と```git revert```についてまとめました。
開発初心者の方が一番最初につまずくのがgit周りなんじゃないでしょうか…（少なくとも私は😿）
私も日々経験を積んで、エンジニアとして大成できるよう精進いたします。ご精読ありがとうございました！


# 参考
- [【コード管理/GitHub】ローカルリポジトリとリモートリポジトリの変更履歴に差があり、git pushが実行できない問題を解決する](https://qiita.com/Ayumu-y/items/7fb229f624d458ddacd1)
- [第6話 git reset 3種類をどこよりもわかりやすい図解で解説！【連載】マンガでわかるGit ～コマンド編～](https://www.r-staffing.co.jp/engineer/entry/20191129_1)
- [git pushを取り消す方法](https://qiita.com/S42100254h/items/db435c98c2fc9d4a68c2)
- [緊急時の必須テクニック! git revertで不要な処理を打ち消そう](https://www.sejuku.net/blog/71634)