【Github】とりあえずcommitしているけど、戻り方がわからない『お寿司職人』のあなたへ

# ご挨拶と記事の概要
タイトルはネタです。~~お寿司だけに。~~


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557847/ddb707f7-cbd0-052a-df6e-99e10d0e794e.png" width="200">

現在実務1年目のWebエンジニアです。
よろしくお願いします。

今回は、**git/githubを用いて過去のCommitに戻る方法**を出来るだけわかりやすく、簡単に『お寿司を作る工程』を例にしてお伝えしたいと思います。

間違っている点、こうしたほうがいい点などは随時ご指摘ください。


## 記事の対象者
- gitを使った簡単なバージョン管理、githubへコードのpush等ができる方
- Githubを使っているが、過去のCommitに戻す方法がわからない方
- Commitの戻し方は分かるが、実際怖くてやっていない方
- 戻し方の記事をたくさん読んだが、結局よく分からなかった方　←私です。

## 読むと出来るようになること
今回は抜け漏れ等あるかと思いますが、とりあえず**『編集したコードを以前の状態に戻せる』**ことをゴールにしたいと思います。行為の危険性や細かい注意は、私の記事で概要を理解したのち、各自調査の上、補完していただけますと幸いです。ですから、**細かいオプションなどの説明、例えば `--hard` の意味などには言及しません。**

その際は、ぜひ分かったことを教えて頂けたら嬉しいです。(^_^)

## はじめに
gitで過去のcommitに戻す方法は大きく分けて二つあります。

- `git reset --hard`
- `git revert`

です。今回はこのコマンドを実際に使って、実行したら何が起きるのか、どうやったら過去に戻れるのか試してみましょう。

## git reset --hard編スタート！雛形ファイルを用意して、最初のcommit&pushをしてみる

まず、雛形となるHTMLファイルを作って、gitのリモートリポジトリと繋ぐところまでやります。
Githubのリポジトリを新しく作ったのち、以下のファイルを作りましょう。

```html:sushi/gitsushi.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Git寿司</title>
</head>
<body>
  
</body>
</html>
```

その後sushiディレクトリ内で恒例の`git init`、`git add *`、`git commit -m"最初のコミット"` 
`git remote add origin 【作ったリポジトリのURL】`、`git push -u origin master`をしましょう。

この時のGithubが、以下の通りになっていたらOKです。
![スクリーンショット 2020-08-16 8.39.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557847/8bd10608-9ada-394d-9952-d09bf81b23d0.png)

### git reset --hard編：魚を捌き、ご飯を炊き、二つを合わせて握って完成
これから、3つのコミットとプッシュを行います。

都度、add,commit,pushをすることを心がけてください。一緒にやっていきましょう。

#### ①魚を捌くコードを追加してadd,commit,push

```html:sushi/gitsushi.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Git寿司</title>
</head>
<body>
  <h1>まず、魚を捌きます</h1> <!-- 追加 -->
</body>
</html>
```
このコードを追加したら、`git add *`、`git commit -m"魚を捌きました"`、`git push`を行ってください。

#### ②ご飯を作るコードを追加してadd,commit,push

```html:sushi/gitsushi.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Git寿司</title>
</head>
<body>
  <h1>まず、魚を捌きます</h1> <!-- 追加 -->
  <h2>次に、ご飯を炊きます</h2> <!-- 追加 -->
</body>
</html>
```
このコードを追加したら、`git add *`、`git commit -m"ご飯を炊きました"`、`git push`を行ってください。

#### ③二つを合わせるコードを追加してadd,commit,push

```html:sushi/gitsushi.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Git寿司</title>
</head>
<body>
  <h1>まず、魚を捌きます</h1> <!-- 追加 -->
  <h2>次に、ご飯を炊きます</h2> <!-- 追加 -->
  <h3>最後に、二つを合わせて握ります。</h3> <!-- 追加 -->
</body>
</html>
```
このコードを追加したら、`git add *`、`git commit -m"二つを合わせて、完成しました"`、`git push`を行ってください。

#### ④確認
メニューバーのInsights、Networkからcommitの履歴をみて、こんな感じになっていたら成功です。
![スクリーンショット 2020-08-16 8.54.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557847/2e6844ac-f0f0-536b-642c-2e11100df0db.png)
お寿司が完成しました！

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557847/b4b46e13-0448-4620-86b0-7642093644b5.png" width="100">


### git reset --hard編：早速戻してみる
さて、Git寿司のお寿司職人のあなたは、出来栄えに納得できず、過去に戻ってお寿司を握り直すことにしました。

#### ①Git logで戻るための場所を確認する
過去のCommitに戻るには、まず戻るための情報を得る必要があります。
コマンドラインのツールで`git log`を入力してみてください。

以下の様な表示が出ると思います。ここで、アルファベットと数字の長い文字列がありますが、これを俗にcommit idと言います。正式名称を調べてみましたが、曖昧だったので、今回はcommit idと呼ばせてください。

まずは、戻りたい過去のCommit idをコピーしておきます。

**※以下のコミットIDやメアドは一応、適当に編集しております。**

```
commit 5eae37f995d73c12664c61472b617b2ed (HEAD -> master, origin/master)
Author: yuki-snow1823 <aaaaaa@gmail.com>
Date:   Sun Aug 16 08:53:46 2020 +0900

    二つを合わせて握って、完成しました

commit a0f843e496622b09c0ef838ce1f613acc
Author: yuki-snow1823 <aaaaaa@gmail.com>
Date:   Sun Aug 16 08:48:57 2020 +0900

    ご飯を炊きました

commit 04d1d7cc3c8d488d478278a3afbb87aaa
Author: yuki-snow1823 <aaaaaa@gmail.com>
Date:   Sun Aug 16 08:44:34 2020 +0900

    魚を捌きました

commit 34fdc3f66d2b5c81882d168be5f97bee2
Author: yuki-snow1823 <aaaaaa@gmail.com>
Date:   Sun Aug 16 08:37:30 2020 +0900

    最初のコミット
```

#### ②commit idを指定して戻る

今回は、**ご飯を炊いた後、つまり握る前戻りたい**と思ったとしましょう。
その場合は、`git reset --hard 【ご飯を炊きましたのCommit id】`とします。
具体的には、`git reset --hard a0f843e496622b09c0ef838ce1f613acc`です。
**※皆さんが実際に行う場合は、ご自身の表示されたcommit idを指定してください。**

ここが混乱しやすい部分なのですが、戻った段階を含まない、つまり、それより後ろの変更履歴をなかったことにするのが今回のコマンドです。ですから、握る前ならば、**直前の炊いたという編集をしたcommit idを指定してreset**をします。

では、実際にやって変化を見てみましょう。

**変更前**

```html:sushi/gitsushi.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Git寿司</title>
</head>
<body>
  <h1>まず、魚を捌きます</h1> <!-- 追加 -->
  <h2>次に、ご飯を炊きます</h2> <!-- 追加 -->
  <h3>最後に、二つを合わせて握ります。</h3> <!-- 追加 -->
</body>
</html>
```
この状態で、`git reset --hard a0f843e496622b09c0ef838ce1f613acc`を実行します。

**実行後**

```html:sushi/gitsushi.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Git寿司</title>
</head>
<body>
  <h1>まず、魚を捌きます</h1> <!-- 追加 -->
  <h2>次に、ご飯を炊きます</h2> <!-- 追加 -->
</body>
</html>
```

おめでとうございます！握った部分の変更が消えました。

#### ③新たにcommitしてpushして、Githubを確認する
さて、この変更をadd,commit,pushしてGithub上で何が起きているか確認しましょう。

`git add *`、`git commit -m"やり直し"`、`git push`を行ってください。

...

```
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/yuki-snow1823/gitsushi.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
```

拒否されてしまいました。これは、`git reset --hard`をすることによって、**過去のcommit自体が消えた結果Githubの情報とローカルの情報に齟齬が起きてしまったから**です。

どういう状態か詳しく説明したいので、今回だけ強制的にプッシュ、`git push -f`をやってみましょう。

強制的にpushした結果、変更は反映されましたが、commitの履歴がこんな感じでおかしくなってしまいます。
本来、寿司を握った後にやり直しのcommit,pushをしたので点が5個になるはずが、3個になってしまいました！！！

![スクリーンショット 2020-08-16 9.39.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557847/d7258d3a-968c-e4b3-a834-d07d899f71ad.png)

個人で何かサッと開発するならともかく、他の人と一緒に開発していたとしたら、「え！？」と驚かせちゃいますね。あまり推奨されていないやり方なので、この方法で戻る場合は、注意しましょう。

では、続いて`git revert`編に突入です。


## git revert編スタート！さっきの状態から、お寿司完成までとりあえず雛形を戻す

現在、握る前に強制pushした段階だと思いますので、綺麗に握ってpushし直しましょう。

```html:sushi/gitsushi.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Git寿司</title>
</head>
<body>
  <h1>まず、魚を捌きます</h1> <!-- 追加 -->
  <h2>次に、ご飯を炊きます</h2> <!-- 追加 -->
  <h3>綺麗に二つを握ります。</h3> <!-- 追加 -->
</body>
</html>
```
このコードを追加したら、`git add *`、`git commit -m"綺麗に握り直しました"`、`git push`を行ってください。

Githubを確認して、綺麗に反映されているか確認してください。

### git revert編：git revertとresetの違いを知る
`git revert`は`git reset`と違い、取り消すコミットを上書きする（打ち消す）ようなコミットを新しく作成して、その結果、既存のコミットを取り消すコマンドです。既存コミットの履歴が`git reset`の時のように消えるわけではありません。

実際に見ていきましょう。

### git revert編：resetの時と同様に戻ってみる

とりあえず、`git reset --hard`の時と同様に戻ってみましょう。

現在、`git log`をしてみるとこんな感じです。

```
commit 29d1121eeeef517cf360bca58bd815a403 (HEAD -> master, origin/master)
Author: yuki-snow1823 <aaaaaa@gmail.com>
Date:   Sun Aug 16 09:59:12 2020 +0900

    綺麗に握り直しました

commit a0f843e496622b09c0ef838ce1f613accc
Author: yuki-snow1823 <aaaaaa@gmail.com>
Date:   Sun Aug 16 08:48:57 2020 +0900

    ご飯を炊きました

commit 04d1d7cc3c8d488d478278a3afbb87aaa7
Author: yuki-snow1823 <aaaaaa@gmail.com>
Date:   Sun Aug 16 08:44:34 2020 +0900

    魚を捌きます

commit 34fdc3f66d2b5c81882d168be5f97bee2a
Author: yuki-snow1823 <aaaaaa@gmail.com>
Date:   Sun Aug 16 08:37:30 2020 +0900

    最初のコミット
```

さて、`git revert`はどのように戻るかはまだ内緒にして、とりあえずやってみましょう。
気持ちとしては、`git reset --hard`の時と同様に、**お寿司を握る前、米を炊いた直後に戻りたいとしましょう。**

ではここで、`git revert a0f843e496622b09c0ef838ce1f613accc`を実行してみましょう。

**実行後**
![スクリーンショット 2020-08-16 10.11.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557847/e96d7b49-c6de-510d-82f1-b7bf68b3e877.png)

コンフリクトが起こりました。（VSCodeの場合は、以下の様な画面になると思います。

さて、ここで入力側の変更を取り込む（revert側の変更）と、こんな感じになります。

```html:sushi/gitshushi.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Git寿司</title>
</head>
<body>
  <h1>まず、魚を捌きます</h1> <!-- 追加 -->
</body>
</html>

```
`git reset --hard`の時と、まず違うことがありますよね。

- revertの場合は、打ち消すcommitを作るので、**ご飯を炊いたことすら消えてしまいました**。

つまり、**戻りたい場所のcommit idを指定すると、その時点の編集内容をなかったことにします。（今回はコンフリクトを無理に解決したので、それ以降のコミットの内容も消えていますが、コマンドの意味としては「取り消したいコミットを上書きする（打ち消す）」です。）**

### pushして、確認する
では、`git add *`、`git commit -m"魚を捌いたところまで戻りました"`、`git push`を行ってください。
resetの時と違って、pushの拒否はされません。

Githubを確認しにいきましょう。
![スクリーンショット 2020-08-16 10.23.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/557847/7c6239a7-fe3d-2801-18eb-f28f20dc2e25.png)

ここでも、`git reset --hard`の時と違いがありますね！そう、commitのログがちゃんと続いています。
こうすれば、他の人と共同開発をしていても、何をしたかがわかりますね。ただ、こういう通常とは異なる動作をする場合は、ちゃんと他の人に許可をとってから行いましょう！

## まとめ
- `git reset --hard` または `git revert`でお寿司作りの失敗をやり直せる
- `git reset --hard` は指定したcommit id以降の変更を消し、commitのログも消す
- `git revert` は指定したcommitを打ち消し、commitのログは残す

皆さんも実務や大切な本番に備えて、戻る練習をぜひしてみてください。

## 素晴らしい参考記事

[【gitコマンド】いまさらのrevert](https://qiita.com/chihiro/items/2fa827d0eac98109e7ee)

[gitで特定のcommitバージョン/リビジョンを指すアレをなんと呼ぶか問題](https://qiita.com/bigwheel/items/0b331451558637ee29b3)

[[git reset (--hard/--soft)]ワーキングツリー、インデックス、HEADを使いこなす方法](https://qiita.com/shuntaro_tamura/items/db1aef9cf9d78db50ffe)

[7.7 Git のさまざまなツール - リセットコマンド詳説](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%81%95%E3%81%BE%E3%81%96%E3%81%BE%E3%81%AA%E3%83%84%E3%83%BC%E3%83%AB-%E3%83%AA%E3%82%BB%E3%83%83%E3%83%88%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E8%A9%B3%E8%AA%AC)

## 弊社の紹介
私は現在、[株式会社ダイアログ](https://www.dialog-inc.com/)という物流×technologyの会社に勤務しております。
2020年9月現在、エンジニアの募集はしていませんが、他にも様々な職種を募集しているので、[Wantedly](https://www.wantedly.com/companies/company_3244501)のページをご覧ください。いつか自分のQiitaきっかけで応募してくださる方がいたら、嬉しいなと思います。

[インタビュー記事（入社後の感想など）](https://www.wantedly.com/companies/company_3244501/post_articles/278203)