1. 概要

このチュートリアルでは、さまざまなツールを使用してgitリポジトリのコミット履歴から大きなファイルを削除する方法を学習します。

2. gitfilter-branchを使用する

これは最も一般的に使用される方法であり、コミットされたブランチの履歴を書き換えるのに役立ちます。

たとえば、プロジェクトフォルダ内に誤ってblobファイルをドロップし、それを削除した後も、git履歴にそのファイルがあることに気付いたとします。

$ git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"
* 9e87646        (HEAD -> master) blob file removed
* 2583677        blob file
* 34ea256        my first commit

次のコマンドを使用してツリーとそのコンテンツを書き換えることで、git履歴からblobファイルを削除できます。

$ git filter-branch --tree-filter 'rm -f blob.txt' HEAD

ここでは、 rm オプションツリーからファイルを削除します。 さらに、 -f オプション防ぐプロジェクト内の他のコミットされたディレクトリにファイルがない場合に失敗するコマンド。 -fオプションを指定しないと、プロジェクトに複数のディレクトリがある場合にコマンドが失敗する可能性があります。

コマンドを実行した後のgitログは次のとおりです。

* 8f39d86        (HEAD -> master) blob file removed
* e99a81d        blob file
| * 9e87646      (refs/original/refs/heads/master) blob file removed
| * 2583677      blob file
|/  
* 34ea256        my first commit

HEADをコミット履歴のSHA1キーに置き換えて、書き換えを最小限に抑えることができます。

gitログには、削除されたファイルへの参照がまだ含まれています。 リポジトリを更新することで、参照を削除できます。

$ git update-ref -d refs/original/refs/heads/master

-d オプションは、古い値がまだ含まれていることを確認した後、指定された参照を削除します。

参照が変更されたことをリポジトリに記録する必要があります。

$ git reflog expire --expire=now --all

expire サブコマンドは、古い参照ログエントリを削除します。

最後に、リポジトリをクリーンアップして最適化する必要があります。

$ git gc --prune=now

–prune = now オプションは、年齢に関係なく、緩いオブジェクトを削除します。

コマンドを実行した後、ここにgitログがあります:

* 6f49d86        (HEAD -> master) my first commit

参照が削除されたことがわかります。

または、次を実行することもできます。

$ git filter-branch --index filter 'git rm --cached --ignore-unmatched blob.txt' HEAD

これはまったく同じように機能します ツリーフィルター、 ただし、インデックス、つまり作業ディレクトリのみを書き換えるため、より高速です。 サブコマンド –無視-比類のない プロジェクト内の他のコミットされたディレクトリにファイルがない場合にコマンドが失敗するのを防ぎます。

大きなファイルを削除する場合、2つの異なるコマンドを使用するこのアプローチは遅くなる可能性があることに注意してください。

3. git-filter-repoを使用する

別のアプローチは、git-filter-repoコマンドを使用することです。 これはサードパーティのアドオンであり、使用が簡単で、他のアプローチよりも高速です。 さらに、これはgitの公式ドキュメントで推奨されているソリューションです。

3.1. インストール

少なくともpython3>=3.5およびgit>=2.22.0が必要です。 一部の機能にはgit2.24.0以降が必要です。

Linuxマシンにgit-filter-repoをインストールします。 Windowsインストールガイドについては、ドキュメントを参照してください。 

まず、次のコマンドを使用してpython-pipgit-filter-repoをインストールします。

$ sudo apt install python3-pip
$ pip install --user git-filter-repo

または、以下のコマンドを使用してgit-filter-repoをインストールできます。

# Add to bashrc.
export PATH="${HOME}/bin:${PATH}"

mkdir -p ~/bin
wget -O ~/bin/git-filter-repo https://raw.githubusercontent.com/newren/git-filter-repo/7b3e714b94a6e5b9f478cb981c7f560ef3f36506/git-filter-repo
chmod +x ~/bin/git-filter-repo

3.2. ファイルの削除

コマンドを実行してgitログを確認しましょう:

$ git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"
* ee36517        (HEAD -> master) blob.txt removed
* a480073        project folder

次のことは、リポジトリを分析することです。

$ git filter-repo --analyze
Processed 5 blob sizes
Processed 2 commits
Writing reports to .git/filter-repo/analysis...done.

これにより、リポジトリの状態に関するレポートのディレクトリが生成されます。 レポートはで見つけることができます .git / filter-repo/analysis。 T 彼の情報は、後続の実行で何をフィルタリングするかを決定するのに役立つ場合があります。 また、以前のフィルタリングコマンドが実際に実行したかったことを実行したかどうかを判断するのにも役立ちます。

次に、オプション –path-match、を指定してこのコマンドを実行します。これは、フィルター処理された履歴に含めるファイルを指定するのに役立ちます。

$ git filter-repo --force --invert-paths --path-match blob.txt

これが私たちの新しいgitログです:

* 8940776        (HEAD -> master) project folder

実行後、変更されたコミットのコミットハッシュを変更します。 

4. BRGレポクリーナーの使用

もう1つの優れたオプションは、Javaで記述されたサードパーティのアドオンである BRGRepo-Cleanerです。

gitfilter-branchアプローチよりも高速です。 さらに、大きなファイル、パスワード、クレデンシャル、およびその他のプライベートデータを削除するのに適しています 。 

200MBを超えるblobファイルを削除したいとします。 このアドオンを使用すると、これを簡単に行うことができます。

$ java -jar bfg.jar --strip-blob-bigger-than 200M my-repo.git

次に、このコマンドを実行して、デッドデータをクリーンアップしましょう。

$ git gc --prune=now --aggressive

5. git-rebaseを使用する

このアプローチを使用するには、gitログのSHA1キーが必要です。

$ git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"
* 535f7ea        (HEAD -> master) blob file removed
* 8bffdfa        blob file
* 5bac30b        index.html

私たちの目的は、コミット履歴からblobファイルを削除することです。 したがって、削除するエントリの前にあるエントリの履歴からSHA1キーを使用します。

このコマンドを使用して、インタラクティブなリベースを開始します。

$ git rebase -i 5bac30b

これにより、 nano エディターが開き、次のように表示されます。

pick 535f7ea blob file removed
pick 8bffdfa blob file 

# Rebase 5bac30b..535f7ea onto 535f7ea (2 command)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .  create a merge commit using the original merge commit's
# .  message (or the oneline, if no original merge commit was
# .  specified). Use -c <commit> to reword the commit message.

ここで、「」というテキストを削除してこれを変更します。535f7eablobファイルを削除しました「。 これは、コミット履歴を変更し、以前に削除した履歴を削除するのに役立ちます。

次に、ファイルを保存してエディターを終了すると、ターミナルに次のメッセージが表示されます。

interactive rebase in progress; onto 535f7ea
Last command done (1 command done):
pick 535f7ea blob file removed
No commands remaining.
You are currently rebasing branch 'master' on '535f7ea'.
(all conflicts fixed: run "git rebase --continue")

最後に、リベース操作を続けましょう。

$ git rebase --continue
Successfully rebased and updated refs/heads/master.

次に、コミット履歴を確認できます。

$ git log --graph --full-history --all --pretty=format:"%h%x09%d%x20%s"
* 5bac30b        (HEAD -> master) index.html

このアプローチはgit-filter-repoほど高速ではないことに注意してください。

6. 結論

この記事では、gitリポジトリのコミット履歴から大きなファイルを削除するためのさまざまなアプローチを学びました。 また、gitのドキュメントによると、 git filter-repo が推奨されていることもわかりました。これは、他のアプローチに比べて高速で短所が少ないためです。