GitHubリモートリポジトリに誤ってpushしてしまったコミットログを削除する

# はじめに
リモートリポジトリのコミットログを削除したい時ってありませんか？
本来ならそんなことをしてはいけないのですが、それでもどうしても削除したいときは今回の方法で削除することができます。

# 事前準備
ここではリポジトリを作成し、コミットログを用意します。

# リポジトリの作成
実施するにあたってのリポジトリを作成します。
今回は **samplerepo** というリポジトリ名で行います。
![スクリーンショット 2024-11-25 9.52.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82090/9ec6662a-19ab-27db-3a91-81b084a7b91f.png)

## コミットログ作成（初回）
作成したらまずはコミットログを用意します。

```zsh:clone
% git clone git@github.com:hogehoge/samplerepo.git
Cloning into 'samplerepo'...
warning: You appear to have cloned an empty repository.
```

```zsh:ディレクトリ移動
% cd samplerepo 
```

今回は **README.md** を作成してそれをコミットすることでログを作成します。

```zsh:README.md作成
% echo "# samplerepo" >> README.md
```

```zsh:add
% git add README.md 
```

```zsh:commit
% git commit -m "first commit"
[main (root-commit) 7d796d7] first commit
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
```

```zsh:push
% git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 224 bytes | 224.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:hogehoge/samplerepo.git
 * [new branch]      main -> main
```
以下のように `README.md` が追加されていることが確認できます。

![スクリーンショット 2024-11-25 22.03.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82090/164b32b0-3eb1-b144-e855-40b037be5327.png)


## コミットログ作成（2回目）
今度は **test.txt** を作成するコミットログを残します。

```zsh:test.txt作成
% touch test.txt
```

```zsh:add
% git add test.txt 
```

```zsh:commit
% git commit -m "test.txt"
[main 4e2d57e] test.txt
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 test.txt
```

```zsh:push
% git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 277 bytes | 277.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:hogehoge/samplerepo.git
   7d796d7..4e2d57e  main -> main
```

**test.txt** というファイルが追加されていることが確認できます。

![スクリーンショット 2024-11-25 22.04.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82090/163bc4ec-a651-83bc-b0af-7b3d51ddab55.png)

また、コミットログを確認すると2つのログが存在することも確認ができます。

![スクリーンショット 2024-11-25 22.05.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82090/e37d92b2-dcca-d129-31ff-dcc96faa3ec5.png)

以上で準備完了です。


# コミットログの削除
それではコミットログを削除したいと思います。
今回は最初にコミットした **「first commit」** というログを削除します。

## コミットログの確認
現在は以下のようなコミットログがローカルリポジトリで確認ができると思います。

```zsh:log
% git log
commit 4e2d57e85ca9d73816973639049492b4056d22fe (HEAD -> main, origin/main)
Author: Kohei Yamada <kohei@hengjiu.jp>
Date:   Mon Nov 25 22:04:34 2024 +0900

    test.txt

commit 7d796d79c6f92e983fc1d68c3a65a8b31f573cfd
Author: Kohei Yamada <kohei@hengjiu.jp>
Date:   Mon Nov 25 22:02:07 2024 +0900

    first commit
```

削除ではここで確認できるハッシュ値を使います。


## コミット履歴の削除

先ほど確認した **「first commit」** のハッシュ値を使って以下のようにコマンドを実行します。
`7d796d79c6f92e983fc1d68c3a65a8b31f573cfd` の部分は適宜置き換えてください。

```zsh:コミット履歴削除
% git filter-branch --commit-filter '
    if [ "$GIT_COMMIT" = "7d796d79c6f92e983fc1d68c3a65a8b31f573cfd" ]; then
        skip_commit "$@";
    else
        git commit-tree "$@";
    fi' HEAD
WARNING: git-filter-branch has a glut of gotchas generating mangled history
	 rewrites.  Hit Ctrl-C before proceeding to abort, then use an
	 alternative filtering tool such as 'git filter-repo'
	 (https://github.com/newren/git-filter-repo/) instead.  See the
	 filter-branch manual page for more details; to squelch this warning,
	 set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...

Rewrite 4e2d57e85ca9d73816973639049492b4056d22fe (2/2) (0 seconds passed, remaining 0 predicted)    
Ref 'refs/heads/main' was rewritten
```

改めてログを確認するとローカルリポジトリでは削除されていることが確認できます。

```zsh:log
% git log                            
commit 04d43daf983095854a595126b484cd5d4561eb05 (HEAD -> main)
Author: Kohei Yamada <kohei@hengjiu.jp>
Date:   Mon Nov 25 22:04:34 2024 +0900

    test.txt
```

この状態でリモートリポジトリへプッシュします。
通常のプッシュだとエラーになるため `--force` オプションを付けて強制的にプッシュします。

```zsh:push
% git push --force
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (4/4), 266 bytes | 266.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
To github.com:hogehoge/samplerepo.git
 + 4e2d57e...04d43da main -> main (forced update)
```

リモートリポジトリでも履歴が削除されていることが確認できました。

![スクリーンショット 2024-11-25 22.07.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82090/8fb31917-571f-5db6-ebed-fa2d20eadb4e.png)

以上でコミットログの削除については以上となります。


# コミットログを全て削除してみる
気になったので試してみたいと思います。
もう一つのコミットログも同じ方法で削除してみます。

```zsh:コミット履歴削除
% git filter-branch --commit-filter '
    if [ "$GIT_COMMIT" = "04d43daf983095854a595126b484cd5d4561eb05" ]; then
        skip_commit "$@";
    else
        git commit-tree "$@";
    fi' HEAD

WARNING: git-filter-branch has a glut of gotchas generating mangled history
	 rewrites.  Hit Ctrl-C before proceeding to abort, then use an
	 alternative filtering tool such as 'git filter-repo'
	 (https://github.com/newren/git-filter-repo/) instead.  See the
	 filter-branch manual page for more details; to squelch this warning,
	 set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...

Cannot create a new backup.
A previous backup already exists in refs/original/
Force overwriting the backup with -f
```
無事に削除できたのかなと思いきや、

> Cannot create a new backup. A previous backup already exists in refs/original/

というメッセージが表示されています。

このエラーについて調べたところ、Gitのバックアップシステムに関する問題とのことです。

Gitではフィルタリング操作（例えば `git filter-branch` を使った履歴の変更）を行うと、元の参照が `refs/original/` にバックアップされます。ですが、今回のような対応を行った後だとすでにバックアップが存在してしまうため、再度実施しようとすると新しいバックアップを作成できずこのエラーが発生しまうとのことです。

実際に `git log` で確認したところコミット履歴は削除されていないことがわかります。

```zsh:log
% git log                            
commit 04d43daf983095854a595126b484cd5d4561eb05 (HEAD -> main)
Author: Kohei Yamada <kohei@hengjiu.jp>
Date:   Mon Nov 25 22:04:34 2024 +0900

    test.txt
```

これを解消するためにはバックアップを削除する必要があります。

```zsh:バックアップ削除
% rm -rf .git/refs/original/refs/heads/main
```

それでは改めて履歴の削除を行います。

```zsh:コミット履歴の削除
% git filter-branch --commit-filter '       
    if [ "$GIT_COMMIT" = "4e2d57e85ca9d73816973639049492b4056d22fe" ]; then
        skip_commit "$@";
    else
        git commit-tree "$@";
    fi' HEAD
WARNING: git-filter-branch has a glut of gotchas generating mangled history
	 rewrites.  Hit Ctrl-C before proceeding to abort, then use an
	 alternative filtering tool such as 'git filter-repo'
	 (https://github.com/newren/git-filter-repo/) instead.  See the
	 filter-branch manual page for more details; to squelch this warning,
	 set FILTER_BRANCH_SQUELCH_WARNING=1.
Proceeding with filter-branch...

Rewrite 4e2d57e85ca9d73816973639049492b4056d22fe (1/1) (0 seconds passed, remaining 0 predicted)    
Ref 'refs/heads/main' was deleted
fatal: Not a valid object name HEAD
```

無事に削除ができたみたいです。

```zsh:log
% git log
fatal: your current branch 'main' does not have any commits yet
```
今度は
> fatal: Not a valid object name HEAD

というメッセージが表示されましたが、これは Git が現在のリポジトリに有効な HEAD がない（ HEAD がどのブランチやコミットも指していない）という意味になります。
今回の処理で履歴を全部削除してしまった状態なのでこのメッセージが表示されたようです。

実際にbranchを確認してみると何も表示されません。

```zsh:branch確認
% git branch
```

履歴を全て削除したことで HEAD がどれを指しているかの履歴も消えてしまったようです。
この場合は pull をすれば元に戻ります。

```zsh:pull
% git pull
```

```zsh:branch
% git branch
* main
```

ということでコミットログは削除できるが全てのコミットログを削除することはできないということがわかりました。


# おわりに
普段はブランチ切ってコミットしてマージしてという単純なことしか行ってなかったので、今回のようなことをきっかけに Git への知識が少し深まったかなと思います。


#### 参考
https://git-scm.com/docs/git-filter-branch