【Git】ファイルを間違えてプッシュした時の解決法

先日、間違ったファイルをリモートブランチにプッシュしてしまいました。

何とか解決できたので、その時のコマンドの流れを忘れないように残しておこうと思います。

# ここで登場する用語

参考記事

>[Git超絶まとめ](https://qiita.com/masashi127/items/2e103c3fba9d1b058961)

## ワーキングツリー
- ユーザーが作業しているディレクトリ領域

## ステージング
- コミット対象のファイルを登録する領域
- `git add`でステージングできる 
- インデックスとも言うらしい

## HEAD
- 今いるブランチの最新コミット
- HEAD^は１つ前のコミット

# 間違ったファイルをプッシュした

ここでは、`push_test_01.txt`をリモートブランチにプッシュする必要がなかった（もしくはプッシュしてはいけなかった）とします。

```bash
$ git add .
$ git status
On branch develop
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   push_test_01.txt
	new file:   push_test_02.txt
	modified:   test_01.txt
```

```bash
$ git commit -m "テスト3"
```

```bash
$ git push origin develop
```

間違えてプッシュしてしまいました。

# こんな感じに戻したい
目的はこの2つです。

- テスト3としてコミットする前の状態（テスト2の状態）に戻したい
- けれど、ワークツリーの変更（push_test_01.txt, push_test_02.txt, test_01.txtの変更）はそのまま残したい

# ログの確認

`git log`でログを確認します。

プッシュを取り消すには、まずコミットを取り消す必要があります。
取り消すと言うより、過去の状態に戻すと言ったほうが正しいかもしれません。

ここでの巻き戻し先は、テスト2です。

```bash
$ git log -3
commit コミットID (HEAD -> develop, origin/develop)
Author: author_name <author@example.com>
Date:   Sun Dec 9 17:34:33 2018 +0900

    テスト3

commit コミットID
Author: author_name <author@example.com>
Date:   Sun Nov 18 16:31:46 2018 +0900

    テスト2

commit コミットID
Author: author_name <author@example.com>
Date:   Sun Nov 18 16:08:16 2018 +0900

    テスト1
```

# コミットの巻き戻し

`git reset`でコミットを巻き戻すことができます。
コミットIDは、戻したい時点のコミットIDを指定します。

ここではテスト2の状態に戻したいので、テスト2のコミットIDを指定します。

```bash
$ git reset --soft テスト2のコミットID
```

`softオプション`を指定すると、ワークツリーの変更とステージングの状態はそのまま残ります。`hardオプション`を指定すると、どちらの内容も消えます。

デフォルトは`mixedオプション`です。（ワークツリーの変更は残るが、ステージングの状態は戻る。）

コマンド実行後、必ず`git log`でログを確認します。
テスト3のコミットが消えました。

```bash
$ git log -3
commit コミットID
Author: author_name <author@example.com>
Date:   Sun Nov 18 16:31:46 2018 +0900

    テスト2

commit コミットID
Author: author_name <author@example.com>
Date:   Sun Nov 18 16:08:16 2018 +0900

    テスト1

commit コミットID
Author: author_name <author@example.com>
Date:   Sun Nov 18 16:08:16 2018 +0900

    テスト0
```

ワークツリーもそのままです。ファイルを変更した後、ステージングされた状態（`git add`後の状態）に戻っています。

```bash
$ git status
On branch develop
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   push_test_01.txt
	new file:   push_test_02.txt
	modified:   test_01.txt
```

# プッシュしたくないファイルをステージングから除く
プッシュしたくないファイルをステージングから除きます。

`git reset HEAD ファイル名`を実行します。

```bash
$ git reset HEAD push_test_01.txt
```

```bash
$ git status
On branch develop
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

	new file:   push_test_02.txt
	modified:   test_01.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	push_test_01.txt
```

# 再コミット
正しくステージングがされていることを確認したら、もう一度コミットします。

```bash
$ git commit -m "テスト3 その2"
```

# 再プッシュ

プッシュをもう一度行います。

`fオプション`を使用して強制的にプッシュします。

```bash
$ git push -f origin develop
```

これでプッシュしたくないファイルは、リモートリポジトリから消えました。

# プッシュしなかったファイルは?

今回の`push_test_01.txt`は追跡されていないファイルなので、削除したり`.gitignore`に記述したりします。

`.gitignore`については、こちらの記事が参考になります。

>[[Git] .gitignoreの仕様詳解](https://qiita.com/anqooqie/items/110957797b3d5280c44f)

追跡されているファイルの場合、変更（ワークツリーにある状態）自体を取り消したい場合は、`git checkout ファイル名`でHEAD（最新コミット）の状態に戻すこともできます。

```bash
$ git checkout test_01.txt
```

この場合、既にコミットされている状態に戻るため、コマンド実行後は`git status`に表示されることはありません。

また、コミットIDを指定して、特定のコミットの状態に戻すこともできます。

# おまけ
コミットはしたくないけれどブランチを切り替えたい場合は、`git　stash -u`をよく使います。

ファイルの一時退避に使えるコマンドです。`uオプション`で、新規作成のファイルも対象に含める事ができます。

気を抜くと、ブランチもスタッシュも乱立されています・・・適宜削除しなければです。

参考記事

>[【git stash】コミットはせずに変更を退避したいとき](https://qiita.com/chihiro/items/f373873d5c2dfbd03250)