Git で歴史改変したいとします。もっと限定して、これまでのすべてのコミットから特定のファイルの存在を抹消したいとします。それについては参考文献[1]以上にここに記すことがありません。Windows 上の Git Bush(git-filter-repo に必要な Python3 もインストールされているとします)で以下の手順でファイルの存在を抹消できます(ただし注意すべき事項があります;後述)。
- git-filter-repo というファイルを echo $PATH で出てくる適当な場所に配置します。
- 存在を抹消したいファイルを含むリポジトリを新しくどこかに clone します。
- git filter-repo --path 存在を抹消したいファイル --invert-paths で存在の抹消を実行します。
- そうすると .git/config 内の remote と branch が安全のため消されるようなので直接編集で復元します。
- git push -f で存在の抹消をリモートに反映します。
ここまでは問題ないはずです。意図通りにリモートの全コミットから存在を抹消したかったファイルが消えます。が、上の手順は手順の 2 番目に記したように新しい作業ディレクトリで実行したため、以前までの作業ディレクトリがそのままになっています。そして、以前までの作業ディレクトリで以下のように作業を進めてしまったとします。すると悲しい結果になります。
- 以前までの作業ディレクトリで作業し、何らかの変更を git commit します。
- 変更をリモートに反映するべく git push します。するとリジェクトされます。
- 思わず git pull します。するとこれも fatal: refusing to merge unrelated histories となって失敗します。
- 困ったあなたはこのエラーメッセージで検索して「--allow-unrelated-histories オプションを付けましょう」と提示するサイトを多数発見し、git pull --allow-unrelated-histories します。今度はエラーになりませんがコンフリクトが発生します。
- git checkout --ours 変更したファイル でコンフリクトを解消し、git add → git commit → git push します。すると、リモートに変更が反映されると共に、リモートで存在を抹消したはずのファイルが復活します。
というわけで、あなたは再び件のファイルの存在を抹消しなければなりません。
思わぬ結果を回避するためには、新しい作業ディレクトリからリモートの歴史改変を意図通りに行うことができたのを確認した時点で、以前までの作業ディレクトリを廃棄するのにこしたことはないと思います(以前までのディレクトリをきれいに移行する方法もあるかもしれないですがわかりません)。無論、リポジトリから存在を抹消したファイルが手元では大切に保管すべきものである場合(しばしばそう)、一緒くたに廃棄しないでください。
なお、git filter-repo で歴史改変する場合の注意事項(というか歴史改変する時点で注意事項しかないのですが)として、コミット署名が消えます(以下)。コミットに署名していて消えると不都合がある場合は注意してください。
- https://github.com/newren/git-filter-repo/blob/0cd8a1fd392b329df05f05babd942f7fc4318a72/Documentation/git-filter-repo.txt#L1280-L1282
- commits get rewritten meaning they will have new hashes; therefore, signatures on commits and tags cannot continue to work and instead are just removed (thus signed tags become annotated tags)