【git事故】機密ファイルをgithubから削除

# gitでつまづきました

githubにfirebaseのアクセス情報が載っているfirebase.ts始め
各config用ファイルがgithubにて公開されてしまう事故が発生。

**原因 : .gitignoreが正しく設定されていませんでした。**

つまりは.gitignoreの中に
~~~
client/src/lib/
~~~

の記載がなかったことが直接的な原因

これであれば追加したら完了すると思ったが、.gitignoreは新規追加されたファイルを追跡しない様にするためのファイルであり、すでに追跡対象としたファイルには適用されませんでした。

:::note
当時の状況のまとめ :
~~~
client/src/lib/
~~~
はすでにローカルにて追跡対象となっている。さらにgithub上にもアップロードされている
:::

:::note info 
追跡対象かどうかを調べるコマンド
```git ls-files client/src/lib```
これで追跡されているファイル名が帰ってきたら、追跡されているということです。
:::

## 過去の失敗談 : github上でファイルを削除

今回は対応として避けることができた事象ですが紹介します。
公開してはいけないファイルを後悔してしまった私はgithubでファイルの削除をしました。

その後、ローカルのファイルを push しようとするとコンフリクトが起きて失敗します。

```
! [rejected] main -> main (fetch first) error: failed to push some refs to 'origin/main'
```

githubの状態とローカルの状態がズレてしまったことが原因です。この様なコンフリクトが起きないようにgithubのリモートリポジトリでは操作しない様にすることが重要です。

:::note info
＊この事象が起きた場合の対処 :
これに対しては、リモートリポジトリの状態を正としてローカルリポジトリを同期させることが有効です。ローカルファイルで何かしらの変更を加えてたらコンフリクトを解消する必要がありますが。。。。
```
リモート状態をローカルに上書き
git pull origin main
```
:::


### 最適解ではものの、今回行ったこと :
結論、```リモートリポジトリ削除 & ローカルリポジトリ再形成``` をしました。

作業ステップ
1 - githubでリモートリポジトリを削除
2 - ローカルで .gitフォルダを物理削除
3 - githubで新リポジトリを作成
4 - ローカルでgit init 笑

作業途中でどうしたわけかリモートリポジトリへの接続が切れてしまい、それまでの格闘もあったので時間を優先して今回の対応にしました。
(原因は判明済 => filter-repoの仕様によるもの)

:::note warn
業務上だと問題になる対応ですのでお勧めはしないです。小規模な個人開発だったので時間優先で対応しました。
:::

同じ状況になった時、次はもっと失うものを少なくできる様になればという気持ちでこの文章を書いています(11PM)

## 次はbetterな対策をするためのメモ :
まず知らないといけないのが、gitは履歴管理されていて過去にステージングしたファイルは全て残っているということです。またこれはローカル環境のみならず、githubのリモートリポジトリ上でも同じということです。

そして、**.gitignoreはすでに追跡しているファイルには適応されない**ので、知らず知らずの内にpushするとgithubに公開されてしまいます。

:::note info
状況のまとめ
1 - ファイルはgitブランチの履歴上に綺麗に残っている
2 - gitignoreはすでに追跡しているものには適用されない
:::

ここで私たちの取り組むべきは次の方針になります。

**１- gitブランチの履歴上からファイルを削除する**
**2 - .gitignoreを適用して二度とステージングされない様にする**

## gitブランチの履歴上からファイルを削除する
### 事前知識
#### filter-repoライブラリ

このライブラリですべてのブランチから特定のファイル（フォルダ）の履歴を削除することができます
```
brew install git-filter-repo
git filter-repo --path 対象のファイルpath --invert-paths
```
:::note warn
注意点として、今回の私が諦めた原因である「リモートリポジトリとの接続が切れる」事象はこのコマンドによるものでした。よくよくみたらこんなメッセージが返ってきてたのです。

```
~/De/d/L/R/ECommerce main +4 !1 > git filter-repo --path client/src/lib --invert-paths --force NOTICE: Removing 'origin' remote; see 'Why is my origin removed?'
```

履歴を削除した状態でリモートリポジトリにpushされると、リモートリポジトリの状態と、ローカルの状態がコンフリクトを起こしてしまうのでこれを避けるための仕様だそうです。
:::

#### ミラーオプションをつけたクローン
*ここからは私が調べた結果を記載してます。まだ未検証なので以下の操作によるいかなる損害も保証はできませんが、読者様の参考になれば幸いです。*

githubのリポジトリ状態をローカルに落としてくることを **クローン** といいます。(似た様なものでpullがありますが、これはローカル状態を最新状態に上書きする時に使います)

普通はクローンをした時は、リモートリポジトリのその時の状態をローカル環境にコピーして新しいリポジトリとして利用できる様になります
```
git clone <URL>
```


ここでミラーオプション ( ```–-mirror``` )をつけてクローンすると、履歴・ブランチ・タグを含めた全ての参照をまるまるローカル環境にコピーします。
```
git clone --mirror <URL>
```

#### フォースをつけたプッシュ
通常 push をした時、ローカルの状態とリモートの状態に対立があるとエラーが起きてpushが実行されないことがあります。

```
git push origin main
```

一方、--forceオプションをつけることで、リモートの状態にかかわらずローカルの状態を一方的に上書きすることができる様になります。
```
git push --force origin main
```
:::note warn
注意点としては、リモートの履歴状態があなたのローカルの履歴に置き換わるので、チームメンバーがリモート履歴を辿りながら作業して push したら、履歴の相違によりpush実行ができなくなるという事象が発生するかもしれません。
:::

### 作業内容
***やりたいこと => 機密ファイルをgithubに公開してしまったので他人からアクセスできない様に削除をしたい***

#### STEP1 : 方針を決める
ローカルの履歴を正とするか、リモートの履歴を正とするかで方針が変わります。
※チームで開発してる時はリモートの履歴を正とする形になると思います。

そこで今回はリモートの履歴を正とする形での対応をしたに記載したいと思います。

#### STEP2 : リモートリポジトリを --mirror クローンでコピー
リモートリポジトリの履歴情報を含む全ての参照をローカルにコピーします
```
git clone --mirror <URL>
```

#### STEP3 : filter-repoライブラリを使って履歴から対象のフォルダを排除

まずはpipかbrewなどを使って filter-repoライブラリをインストールします
```
brew install git-filter-repo
```

その後次のコマンドで履歴から排除するPATHを指定し実行します。
```
git filter-repo -path <対象への相対PATH>  -invert-paths
```

これでクローンしたリモートリポジトリの履歴から、
対象のファイル情報が排除されました。

#### STEP4 : .gitignoreへの追記
タイトルの通りやることはファイルの編集なんですが次の点だけお気をつけください

**.gitignoreは.gitフォルダと同階層に配置**
フォルダ下でもいいらしいですが、私はうまくできませんでした。

**.gitignoreの記載方法**
記載方法は以下の通りに行います。まあ難しくないので大丈夫だと思いますが

フォルダ下全体をignore : 
```
./path/to/folder/
```

指定したファイルをignore : 
```
./path/to/file/filename.txt
```

#### STEP5 : gitの対象の追跡状態を削除する
.gitignoreはすでに追跡状態のファイルには適用されないので追跡状態を解除する必要があります。

追跡状態の削除(キャッシュの削除)
```
git rm -cached <ファイル名・PATH>
```

これで .gitignore に追加したファイル名・フォルダを追跡対象から除外してくれます


#### STEP6 : 改善したリポジトリをリモートへpush
以上の作業が終わりましたらリポジトリをgithubにあるリモートリポジトリにpushしていきましょう。

filter-repoの仕様でおそらくリモートリポジトリへの接続が切れているので再接続しましょう。
```
git remote add origin https://github.com/lovehakumai/sample.git
```
既に今のローカルリポジトリとリモートリポジトリの履歴には差異があるので、ローカルリポジトリから –forceオプションを使って pushします。
```
git add commit -m “remove secret files”
git push --force origin main
```

これでファイルの削除は完了です。

:::note info
ファイルの削除前までにご自身で行った作業内容は元々のローカルリポジトリに残っているので復元もなんとかなりそうですね!
:::

### 今回の作業の注意点

これがチームプロジェクトだった場合、一旦ブランチにもよりますが多数のメンバーの作業を止めることになるので、報告事項とコミュニケーションは気をつけたいですね。

作業が止まる理由は、リモートリポジトリの履歴に変更が生まれることによるpushのエラーです。


例えばチームで同じリポジトリ上で作業しているならば、あなたと同じ様にチームメイトはリモートリポジトリをクローンして新しくローカルリポジトリを作成し、今までローカルでやった作業を復元しないといけなくなります。

なので、

・githubは基本private
・リモートリポジトリを直接触らない
・機密ファイルは精査 & push前にls-filesで追跡状態を確認

するなどで今回の事故を事前回避できる様にしたいですね。

:::note 
また、公開してしまったAPIキーなどは再発行して使い回ししない様に注意です
:::

以上です。ありがとうございました!