リポジトリサイズの削減

Git リポジトリは時間とともに大きくなります。Git リポジトリに大きなファイルが追加された場合:

  • ファイルをダウンロードしなければならないので、リポジトリの取得が遅くなります。
  • サーバーのストレージスペースを大量に消費します。
  • Git リポジトリのストレージの制限に達する可能性があります。

リポジトリを書き換えることで、不要な履歴を削除してリポジトリを小さくすることができます。** git filter-branchBFGよりもgit filter-repo** をおすすめします。

caution
リポジトリ履歴の書き換えは破壊的オペレーションです。始める前に必ずリポジトリのバックアップを取ってください。リポジトリをバックアップする最善の方法は、プロジェクトをエクスポートすることです。

リポジトリサイズの計算

リポジトリのサイズは、リポジトリ内のすべてのファイルの累積サイズを計算することで求められます。リポジトリのハッシュ化されたストレージパスで du --summarize --bytes を実行するのと似ています。

リポジトリ履歴からファイルを削除

GitLab はハウスキーピングの一環として、到達不可能なオブジェクトを削除します。GitLab では、リポジトリのディスクサイズを手動で小さくするには、まずブランチやタグGitLab が作成したその他の内部参照 (refs) から大きなファイルへの参照を削除する必要があります。これらの参照には次のようなものがあります:

  • refs/merge-requests/*
  • refs/pipelines/*
  • refs/environments/*
  • refs/keep-around/*
note
それぞれの参照についての詳細は、GitLab 固有の参照を参照してください。

これらの参照は自動的にはダウンロードされず、非表示の参照は宣伝されませんが、プロジェクトのエクスポートを使ってこれらの参照を削除することができます。

caution
このプロセスは、リポジトリからパスワードや鍵のような機密データを削除するのには適していません。ファイルの内容を含むコミットに関する情報はデータベースにキャッシュされ、リポジトリから削除された後も表示されます。

GitLab リポジトリからファイルをパージするには:

  1. サポートされているパッケージマネージャを使うか、ソースからgit filter-repo とオプションでgit-sizer をインストールします。

  2. プロジェクトから新しいエクスポートを作成し、ダウンロードします。このプロジェクトエクスポートには、リポジトリのバックアップコピーと、リポジトリからファイルをパージするために使用できる参照が含まれています。

  3. tar を使ってバックアップを解凍します:

    tar xzf project-backup.tar.gz
    

    これにはgit bundleで作成されたproject.bundle ファイルがコンテナとして含まれています。

  4. --bare--mirror オプションを使って、バンドルからリポジトリの新しいコピーをクローンします:

    git clone --bare --mirror /path/to/project.bundle
    
  5. project.git ディレクトリに移動します:

    cd project.git
    
  6. バンドルファイルからのクローンでは、origin リモートがローカルのバンドルファイルに設定されるため、リポジトリの URL に変更してください:

    git remote set-url origin https://gitlab.example.com/<namespace>/<project_name>.git
    
  7. git filter-repo またはgit-sizer のいずれかを使用して、リポジトリを分析し、結果をレビューして、パージするアイテムを決定します:

    # Using git filter-repo
    git filter-repo --analyze
    head filter-repo/analysis/*-{all,deleted}-sizes.txt
       
    # Using git-sizer
    git-sizer
    
  8. 関連するgit filter-repo オプションを使用して、リポジトリの履歴をパージします。一般的なオプションは以下の2つです:

    • --path--invert-paths で特定のファイルをパージできます:

       git filter-repo --path path/to/file.ext --invert-paths
      
    • --strip-blobs-bigger-than を選択すると、例えば10M以上のファイルをすべてパージします:

       git filter-repo --strip-blobs-bigger-than 10M
      

    より多くの例と完全なドキュメントはgit filter-repo ドキュメント を参照してください。

  9. 内部参照を削除しようとしているため、どの内部参照を削除すべきかを知るためには、各実行によって生成されるcommit-map ファイルが必要です。git filter-repo を実行するたびに新しいcommit-map, が commit-map作成さcommit-mapれ、 commit-map前の実行のcommit-mapものが上書きさ commit-mapれます。次のコマンドを使用して、各commit-map ファイルをバックアップできます:

    cp filter-repo/commit-map ./_filter_repo_commit_map_$(date +%s)
    

    git filter-repo コマンドを実行するたびに、この手順とそれに続くすべての手順(リポジトリのクリーンアップ手順を含む)を繰り返してください。

  10. 変更を強制的にプッシュするには、ミラーフラグを解除する必要があります:

     git config --unset remote.origin.mirror
    
  11. 変更を強制プッシュして GitLab 上のすべてのブランチを上書きします:

    git push origin --force 'refs/heads/*'
    

    ブランチが保護されていると、これは失敗します。ブランチの保護を解除してからプッシュし、保護されたブランチを再度有効にしてください。

  12. タグ付きリリースから大きなファイルを削除するには、変更を GitLab のすべてのタグに強制プッシュしてください:

    git push origin --force 'refs/tags/*'
    

    タグが保護されていると、これは失敗します。続行するには、タグの保護を解除してプッシュし、保護されたタグを再度有効にする必要があります。

  13. 存在しなくなったコミットへのデッドリンクを防ぐには、git filter-repo によって作成されたrefs/replace をプッシュしてください。

    git push origin --force 'refs/replace/*'
    

    この仕組みについては、Gitreplace のドキュメントを参照ください。

  14. 次のステップに進む前に、少なくとも30分は待ちましょう。
  15. リポジトリのクリーンアップを実行します。この処理は、30分以上前のオブジェクトのみをクリーンアップします。詳細については、解放されない領域を参照してください。

リポジトリのクリーンアップ

リポジトリのクリーンアップでは、オブジェクトのテキストファイルをアップロードすると、GitLabがこれらのオブジェクトへのGit内部参照を削除します。git filter-repo を使って、リポジトリのクリーンアップで使えるオブジェクトのリスト(commit-map ファイル)を作成することができます。

GitLab 13.6で導入された、リポジトリを安全にクリーンアップするには、オペレーション中は読み取り専用にする必要があります。これは自動的に行われますが、書き込みが進行中であればクリーンアップリクエストの送信は失敗します。そのため、続行する前に未処理のgit push オペレーションをキャンセルしてください。

caution
Git 内部参照を削除すると、関連するマージリクエストコミット、パイプライン、変更の詳細が利用できなくなります。

リポジトリをクリーンアップするには

  1. リポジトリのプロジェクトに移動します。
  2. 設定 > リポジトリに移動します。
  3. オブジェクトのリストをアップロードします。例えば、filter-repo ディレクトリにあるgit filter-repo によって作成されたcommit-map ファイルです。

    commit-map ファイルが 250 KB または 3000 行を超える場合は、ファイルを分割してアップロードできます:

    split -l 3000 filter-repo/commit-map filter-repo/commit-map-
    
  4. クリーンアップを開始]を選択します。

これです:

  • Git 内部での古いコミットへの参照を削除します。
  • git gc --prune=30.minutes.ago をリポジトリに対して実行し、参照されていないオブジェクトを削除します。新しいパックファイルが作成されるまで古いパックファイルが削除されないため、一時的にリポジトリのサイズが大きくなります。
  • プロジェクトにアタッチされている未使用の LFS オブジェクトのリンクを解除し、ストレージスペースを解放します。
  • ディスク上のリポジトリのサイズを再計算します。

GitLab はクリーンアップが完了した後、再計算されたリポジトリサイズをメールで通知します。

リポジトリサイズが減らない場合は、過去30分間に行われた Git オペレーションで参照されたオブジェクトが残っていることが原因かもしれません。リポジトリが 30 分以上休止してから、この手順を再実行してみてください。

リポジトリのクリーンアップを使うときは、次のことに注意してください:

  • プロジェクトの統計情報はキャッシュされます。ストレージ使用量の削減を確認するには、5~10分待つ必要があるかもしれません。
  • クリーンアップでは、30分以上前の緩いオブジェクトが削除されます。つまり、過去30分間に追加または参照されたオブジェクトは、すぐには削除されません。Gitalyサーバーにアクセスできる場合は、この遅延をスキップして、git gc --prune=now を実行し、すべての緩いオブジェクトを直ちに削除することができます。
  • この処理によって、GitLabのキャッシュやデータベースから書き換えられたコミットのコピーがいくつか削除されますが、カバレッジにはまだ多くのギャップがあり、コピーの一部はいつまでも残っている可能性があります。インスタンスキャッシュをクリアすることで、それらの一部を削除することができるかもしれませんが、セキュリティの観点からは依存すべきではありません!

ストレージの制限

リポジトリサイズの制限:

プロジェクトがサイズ制限に達した場合、プロジェクトを作成することはできません:

  • プロジェクトにプッシュします。
  • 新しいマージリクエストを作成します。
  • 既存のマージリクエストをマージします。
  • LFS オブジェクトをアップロードします。

まだできます:

  • 新しいイシューの作成。
  • プロジェクトをクローンします。

リポジトリサイズの制限を超える場合は、できます:

  1. 一部のデータを削除します。
  2. 新しいコミットを作成します。
  3. リポジトリにプッシュバックします。

これらのアクションでは不十分な場合は、以下の方法もあります:

  • いくつかのブロブをLFSに移動します。
  • いくつかの古い依存関係の更新を履歴から削除。

残念ながら、このワークフローはうまくいきません。なぜなら、以前のコミットやブロブはまだ存在しているからです。代わりに、履歴を書き換える必要があります。オープンソースのコミュニティメンテンドツールgit filter-repo をお勧めします。

note
git gc が GitLab 側で実行されるまでは、「削除された」コミットや blob はまだ存在しています。また、書き換えた履歴を GitLab にプッシュできなければなりませんが、すでにサイズの上限を超えている場合は不可能かもしれません。

これらの制限を解除するには、自己管理 GitLab インスタンスの管理者が、制限を超えた特定のプロジェクトの制限を増やす必要があります。そのため、常に積極的に制限値を下回るようにしたほうがよいでしょう。リミットを超えてしまい、一時的にリミットを増やすことができない場合、唯一の選択肢となります:

  1. 現地で不要なものをすべて刈り込みます。
  2. GitLabで新しいプロジェクトを作成し、代わりにそれを使い始めましょう。

トラブルシューティング

GUIに表示されるリポジトリの統計が正しくない

表示されるサイズやコミット数がエクスポートされた.tar.gz やローカルリポジトリと異なる場合、GitLab 管理者に強制更新を依頼することができます。

Rails コンソールを使用します:

p = Project.find_by_full_path('<namespace>/<project>')
pp p.statistics
p.statistics.refresh!
pp p.statistics
# compare with earlier values

# An alternate method to clear project statistics
p.repository.expire_all_method_caches
UpdateProjectStatisticsWorker.perform_async(p.id, ["commit_count","repository_size","storage_size","lfs_objects_size"])

# check the total artifact storage space separately
builds_with_artifacts = p.builds.with_downloadable_artifacts.all

artifact_storage = 0
builds_with_artifacts.find_each do |build|
  artifact_storage += build.artifacts_size
end

puts "#{artifact_storage} bytes"

スペースが解放されません

このページで定義されているプロセスは、リポジトリのExporterのサイズを減少させることができますが、Web UIとターミナルの両方で、ファイルシステムの使用量は変更されません。

この処理では、多くの到達不可能なオブジェクトがリポジトリに残ります。それらは到達不可能なため、エクスポートには含まれませんが、ファイルシステムにはまだ保存されています。これらのファイルは、2週間の猶予期間の後に刈り込まれます。プルーニングによってこれらのファイルが削除され、ストレージ使用量の統計が正確になります。

このプロセスを迅速化するには、「到達不能オブジェクトの刈り込み」ハウスキーピングタスクを参照してください。