MarkdownとBullet Journal

いわゆるプログラマーのつぶやき

【Git】リポジトリをコンパクトにする

リポジトリのディスク容量を削減する

開発を何年も継続し続けるとリポジトリもそれなりの規模になる。エンジニアの心理としてはリポジトリを整理したくなるもので、5種類の方法を紹介する。

方法 過去へのアクセス 削減度 作業の手軽さ
git-gc 50%~
partial-clone 90%~
shallow-clone 90%~
logic-compress 90%~
reborn 100%

その1:git gcを用いる

これは最も簡単でそれなりの効果が得られる手段だ。通常Gitはfileをsnapshotして全て保存しているが、git gcを発動するとバイナリfileに差分保存することでディスク容量を圧縮する。オプションを付ける事でさらに圧縮率を上げる事が可能。

programmingforever.hatenablog.com

その2:クローンサイズを下げる:パーシャルクローン

Git のリポジトリが大きくなると、非常に多くのcommit,tree,そしてblob(file)objectを保持する様になる。これは開発者が何年も前の特定commitにいつでも戻れる機能を維持するためであり、すべての到達可能なデータがローカルリポジトリにある事を意味する。しかし、Git の全履歴にある全file(blob)を保持しなくてもよい用途には、保持するデータを限定する「パーシャルクローン」機能がある。名前の通り、必要な一部のobjectのみ保持して、過去のfile参照などの必要が生じたら該当するfileをリモートから自動ダウンロードする機能である。

過去の履歴はほとんど使用せず、追加作業が主であればローカルリポジトリが過去のfile(blob)を保持していなくてもよい実態に合わせたクローンであり、ディスク容量の軽減が可能となる。保持するレベルでプロブレスクローンとツリークローンが選択出来る。

すでにフルクローンがローカルリポジトリにある場合、一旦それを削除してからパーシャルクローンで再度ローカルリポジトリを構成する方法が取れる。

以下比較のために各クローンのローカルリポジトリのobjectの保持状態一覧を示す。

各クローン選択 commit履歴 tree blob
フルクローン 全て 全て 全て
プロブレスクローン 全て 全て 最新のみ
ツリーレスクローン 全て 最新のみ 最新のみ
シャロークローン 最新のみ 最新のみ 最新のみ

フルクローンコマンド:git clone

  • 保持するcommit:全commit
  • 保持するtree:全tree
  • 保持するblob:全blob

パーシャルクローン:ブロブレスクローンコマンド:git clone --filter=blob:none

バーシャルクローン:ブロブレスクローン は、到達可能な全commitとtreeを保持するが、blob(file)は必要に応じて取得する。このクローンは、開発環境に普通に利用出来る。

  • 保持するcommit:全commit
  • 保持するtree:全tree
  • 保持するblob:最新blobのみ

パーシャルクローン:ツリーレスクローンコマンド: git clone --filter=tree:0

バーシャルクローン:ツリーレスクローンは、到達可能な全commitを保持するが、treeとblob(file)は必要に応じて取得する。このクローンは、commit履歴にアクセスしたいビルド環境に向く。

  • 保持するcommit:全commit
  • 保持するtree:最新treeのみ
  • 保持するblob:最新blobのみ

その3:クローンサイズを下げる:シャロークローン

さらに過去のcommitにはアクセスせず、現時点の最新fileだけのビルドだけ行うなどの用途には、シャロークローンが適している。

シャロークローンコマンド: git clone --depth=1

シャロークローンはHEAD以外の全てのcommit履歴も捨てる形でクローンのサイズを小さくする。当然ながら利用可能な Git コマンドが制限される。このクローンは後からのフェッチで過度の作業量を発生するため開発者の使用は推奨できない。

  • 保持するcommit:最新commitのみ(HEAD)
  • 保持するtree:最新treeのみ
  • 保持するblob:最新blobのみ

その4:不要と判断するブランチやblobを整理してgcで高圧縮

ここから下はリポジトリの論理的に不要となった部分を削除する方法を記載する。まずは元のリポジトリに色々と手を加える方法を紹介し、次のその5では過去のリポジトリと絶縁して新生する方法を紹介する。

その4に関しては詳細記事の紹介に留める。

zenn.dev

その5:ゼロから新しいリポジトリを生成

シャロークローンはHEAD以外の過去のcommit履歴を取り込まない事を説明したが、作業ディレクトリに入った最新fileを元に、ゼロから新しいリポジトリを立ち上げる運用も可能だ。何年も経過している様なプロジェクトでも一気にリポジトリがゼロになる。もちろんフルクローンでも同じことが出来るが、作業時間の無駄を避けたいと考えるのがエンジニアであり、本用途にはシャロークローンが適していると考える。

注意:この方法で作る新生リポジトリは元のリポジトリと完全に縁が切れた別物になる点に注意。また元のリポジトリ削除のためにrm -rfコマンドを使うので作業の際はディレクトリの位置などに十分注意すること

$ git clone --depth=1 <url> # - シャロークローンで最新リポジトリから最新file一式を取り込む
$ cd NewDirectory           # - 新しく作成された作業ディレクトリに移動 
$ rm -rf .git/    # - file以外の不要なリポジトリ一式を抹消する
$ git init        # -  最新fileだけの新しいローカルリポジトリを作る
$ git add .       # - 最新fileを全てstaging
$ git commit      # - 新生ローカルリポジトリの最初のcommit

$ git remote add origin <url>  # - 新生のリモートリポジトリのurlを入力
$ git push -u origin main      # - ローカルをリモートにpush

補足:新生に合わせてブランチ名をmainに変えたい場合

昨今の流れからブランチ名をmasterからmainに変更したい場合は下記手順を実行する。

  • ①ローカルのブランチ名をmainに変更(最初にローカルを変えること)
$ git branch -m master main
  • ②リモートのブランチ名をmainに変更
$ git push -u origin main  # - ローカルで変更したmainブランチをリモートへpush
  • GitHubの目的のリモートリポジトリにアクセス
  • ④Settings>Branches>Default branchでmainに変更してUPDATEを押す
  • リポジトリのTOP画面に戻ってブランチ名の欄の中にあるView all branchesボタンを押す
  • ⑤masterを消去(ゴミ箱をクリック)

補足:間違ってrmを実行した場合の復元方法

以下参考までにrmで消去したfileの復元方法を記載するが、プロセスが有効な状態に限られるし、削除したのが.gitフォルダの場合はこの方法では無理だ(代わりに下記外部ツールの利用参照)。いずれにせよそんな事態にならないのが一番

rmコマンドはinodeへのリンクを削除するがinodeは削除しないので、inodeへのリンクがあればデータは存在する。よってfileを扱ったプロセスがまだ存在すれば/proc/【プロセスID】/fd/を辿って復元出来る。

$ rm a.sh   # - fileを削除
$ lsof | grep "a.sh"   # - 削除したfileのプロセスIDを取得
less      8324        hoge    4r      REG  202,1        20 12592058 /home/hoge/a.sh (deleted)

1列目はプロセスに関連付けられたコマンドの名前、2列目がプロセスID、4列目の数字はファイル記述子である(”4r”の”r”は”regular file”、通常fileの意味)。プロセス8324がまだファイルを開いており(望みがある)、file記述子が4で通常アクセス出来るので/procからコピーする。

$ ls -l /proc/8324/fd/4
lr-x------  1 hoge hoge 64 Sep 21 22:31 /proc/8324/fd/4 -> /home/hoge/a.sh (deleted)

$ cp /proc/8324/fd/4 a.sh  # - cpコマンドで復元

上記で無理な場合は外部ツールに頼る方法もある。参考まで。

unskilled.site