web-dev-qa-db-ja.com

Gitを使って大きなバイナリファイルを管理する

私は自分のソースコード(Webアプリケーション)が依存している大きなバイナリファイルの扱い方についての意見を探しています。我々は現在いくつかの代替案を検討しています。

  1. バイナリファイルを手でコピーしてください。
    • Pro:よくわかりません。
    • Contra:新しいサイトを立ち上げたり古いサイトを移行したりするときにエラーが発生する可能性が高くなるため、これには反対です。取るべき別のハードルを構築します。
  2. Git.でそれらをすべて管理します。
    • Pro:重要なファイルをコピーすることを「忘れる」可能性を排除
    • Contra:リポジトリを肥大化させ、コードベースの管理の柔軟性を低下させ、チェックアウト、クローンなどはかなりの時間がかかります。
  3. 別々のリポジトリ。
    • Pro:ソースコードのチェックアウト/クローン作成はこれまでと同様に高速で、イメージは適切に独自のリポジトリにアーカイブされています。
    • Contra:プロジェクト上の唯一のGitリポジトリを持つという単純さを取り除きます。それは確かに私が考えていない他のいくつかのことを紹介しています。

これについてのあなたの経験/考えは何ですか?

また、複数のGitリポジトリとそれらを1つのプロジェクトで管理した経験がある人はいますか。

ファイルはそれらのファイルでPDFを生成するプログラムのための画像です。これらのファイルは(数年の間に)それほど頻繁には変更されませんが、プログラムに非常に関連性があります。プログラムはファイルなしでは動作しません。

513
pi.

プログラムがファイルなしでは動作しない場合は、それらを別々のリポジトリに分割することは悪い考えです。大規模なテストスイートを用意していますが、それらは別々のリポジトリに分割されていますが、それらは本当に "補助的な"ファイルです。

ただし、ファイルを別のリポジトリで管理してから、 git-submodule を使用して適切な方法でプロジェクトに取り込むことができます。それで、あなたはまだあなたのすべてのソースの完全な歴史を持っているでしょう、しかし、私がそれを理解するように、あなたはあなたの画像サブモジュールの1つの関連した改訂を持っているだけでした。 git-submodule機能は、正しいバージョンのコードを正しいバージョンのイメージと一致させるのに役立ちます。

これが良い サブモジュールの紹介 Git Bookからです。

177
Pat Notz

私は最近 git-annex を発見しました。大きなファイルを効率的に管理するために設計されました。写真や音楽などのコレクションに使用します。 git-annexの開発は非常に活発です。ファイルの内容はGitリポジトリから削除することができ、ツリー階層のみがGitによって追跡されます(シンボリックリンクを通じて)。ただし、ファイルの内容を取得するには、引っ張ったり押したりした後に次の手順が必要です。

$ git annex add mybigfile
$ git commit -m'add mybigfile'
$ git Push myremote
$ git annex copy --to myremote mybigfile ## This command copies the actual content to myremote
$ git annex drop mybigfile ## Remove content from local repo
...
$ git annex get mybigfile ## Retrieve the content
## or to specify the remote from which to get:
$ git annex copy --from myremote mybigfile

利用可能なコマンドはたくさんあり、Webサイトには素晴らしいドキュメントがあります。パッケージは Debian で入手可能です。

309
rafak

2015年4月以降の別の解決策は、 Gitラージファイルストレージ(LFS) (GitHubによる)です。

git-lfs を使用します(git-lfs.github.comを参照)。 lfs-test-server
あなたはメタデータをgitリポジトリに、そして大きなファイルを他の場所にのみ保存することができます。

https://cloud.githubusercontent.com/assets/1319791/7051226/c4570828-ddf4-11e4-87eb-8fc165e5ece4.gif

45
VonC

git bup は、大きなバイナリをGitリポジトリにスマートに保存するためのGitの拡張機能です。

あなたはそれをサブモジュールとして持ちたいのですが、リポジトリが扱いにくくなることを心配する必要はありません。サンプルの使用例の1つは、GitにVMイメージを格納することです。

私は実際にはより良い圧縮率を見たことがありません、しかし私のリポジトリはそれらの中に本当に大きなバイナリを持っていません。

あなたのマイレージは異なる場合があります。

30
sehe

git-fat も使えます。私はそれが普通のPythonと rsync だけに依存するのが好きです。また、通常のGitワークフローもサポートしています。次のような自明のコマンドがあります。

git fat init
git fat Push
git fat pull

さらに、あなたはあなたのリポジトリに.gitfatファイルをチェックインし、あなたがgit fatで管理したいファイル拡張子を指定するためにあなたの.gitattributesを修正する必要があります。

通常のgit addを使用してバイナリを追加します。これにより、gitattributesルールに基づいてgit fatが呼び出されます。

最後に、バイナリが実際に格納されている場所をリポジトリやユーザー間で共有できるという利点があり、rsyncが行うことすべてをサポートします。

更新:Git-SVNブリッジを使用している場合はgit-fatを使用しないでください。 Subversionリポジトリからバイナリファイルが削除されてしまいます。ただし、純粋なGitリポジトリを使用している場合は、それは美しく機能します。

27
Carl

私はサブモジュール(Pat Notzとして)または2つの異なるリポジトリを使用します。バイナリファイルを頻繁に変更する場合は、巨大なリポジトリが履歴を消去することによる影響を最小限に抑えるようにします。

数ヶ月前、私は非常によく似た問題を抱えていました。

メインのGitリポジトリで外付けハードディスクドライブを使い、それを各コンピュータにクローンしました。その後、私は習慣的な方法でそれらを分類し始めました(プッシュ、プル、マージ...削除と名前の変更を何度も)。

最後に、私はたった6 GBのMP3ファイルと.gitディレクトリにある83 GBしか持っていませんでした。私はgit-write-treegit-commit-treeを使って、コミット先祖なしで新しいコミットを作成し、そのコミットを指す新しいブランチを始めました。そのブランチの "git log"はコミットを1つだけ示しました。

それから、古いブランチを削除し、新しいブランチだけを残して、ref-logsを削除して、 "git Prune"を実行します。

あなたは同じように時々巨大なリポジトリを "削除"することができます。あなたの "gitクローン"はより速くなるでしょう。

25
Daniel Fanjul

私の意見では、これらの大きなファイルを頻繁に変更する可能性がある場合、またはgit cloneまたはgit checkoutを多数作成する予定の場合は、別のGitリポジトリ(またはこれらのファイルにアクセスする別の方法)の使用を真剣に検討する必要があります。

しかし、私たちのように作業し、バイナリファイルが頻繁に変更されない場合は、最初のクローン/チェックアウトは長くなりますが、その後は必要な速度で実行する必要があります。持っていました)。

12
claf

私が提案したい解決策は、孤立したブランチとタグメカニズムのわずかな悪用に基づいています。今後は* Orphan Tags Binary Storage(OTABS)と呼びます。

TL; DR 12-01-2017githubのLFSやその他のサードパーティを使用できる場合は、ぜひとも使うべきです。あなたができない場合は、次に読んでください。注意してください、この解決策はハックであり、そのように扱われるべきです。

OTABSの望ましい性質

  • それは純粋なgitそしてgitのみの解決策です - それは仕事を他のソフトウェア(git-annexのような)もサードパーティのインフラストラクチャもなしでやらせます(git-annex) githubのLFSのように).
  • それはバイナリファイルを効率的にを保存します、すなわちそれはあなたのリポジトリの歴史を肥大化させません。
  • git pullを含むgit fetchgit fetch --allは、まだ帯域幅効率が良いです。つまり、デフォルトですべての大きなバイナリがリモートから取り出されるわけではありません。
  • Windowsで動作します。
  • すべてのものをsingle gitリポジトリに格納します。
  • (bupとは異なり)削除古いバイナリを許可します。

OTABSの望ましくない性質

  • それはgit cloneを潜在的に非効率的にします(しかしあなたの用法によっては必ずしもそうではありません)。このソリューションを展開する場合は、git clone -b master --single-branch <url>の代わりにgit cloneを使用するよう同僚にアドバイスする必要があるかもしれません。これは、git cloneがデフォルトで文字通りentirerepositoryを複製するためです。参照されていないコミットのように、通常は帯域幅を浪費したくないものも含みます。 SO 4811434 から引用。
  • git fetch <remote> --tagsの帯域幅は非効率的になりますが、必ずしもストレージが非効率的になるわけではありません。あなたはいつでも同僚にそれを使わないように忠告することができます。
  • 不要になったファイルからリポジトリを削除するには、定期的にgit gcトリックを使用する必要があります。
  • それは bupgit-bigfiles ほど効率的ではありません。しかし、それはそれぞれあなたがやろうとしていることや、より既製のものに適しています。あなたは何十万もの小さなファイルやギガバイトの範囲のファイルで問題に遭遇する可能性がありますが、回避策を読んでください。

バイナリファイルを追加する

変更をすべてコミットしたことを確認する前に、作業ツリーが最新のものであり、索引に未確定の変更が含まれていないことを確認してください。何らかの災害が発生した場合に備えて、すべてのローカルブランチをリモート(githubなど)にプッシュすることをお勧めします。

  1. 新しい孤児ブランチを作成します。 git checkout --Orphan binaryStuffがうまくいくでしょう。これにより、他のブランチから完全に切り離されたブランチが作成され、このブランチで最初に作成したコミットには親がなくなり、ルートコミットになります。
  2. git rm --cached * .gitignoreを使用してインデックスを消去してください。
  3. 深呼吸してrm -fr * .gitignoreを使って作業ツリー全体を削除してください。 .gitワイルドカードは一致しないため、内部の*ディレクトリは変更されません。
  4. VeryBigBinary.exeまたはVeryHeavyDirectory /にコピーします。
  5. 追加して&&コミットします。
  6. ブランチとしてリモートにプッシュすると、すべての開発者が次にgit fetchを呼び出して接続を詰まらせるときにダウンロードするようになります。ブランチの代わりにタグを押すことでこれを避けることができます。 git fetch <remote> --tagsと入力する習慣がある場合でも、これは同僚の帯域幅とファイルシステムの記憶域に影響を与える可能性がありますが、回避策をお読みください。進んでgit tag 1.0.0bin
  7. あなたの孤児タグgit Push <remote> 1.0.0binをプッシュしてください。
  8. バイナリブランチを誤ってプッシュすることは決してないので、git branch -D binaryStuffを削除することもできます。コミットを1.0.0binで指し示す孤児タグで十分に生きているため、ガベージコレクションのマークは付けられません。

バイナリファイルをチェックアウトする

  1. VeryBigBinary.exeを現在の作業ツリーにチェックアウトする方法を教えてください。あなたの現在の作業ブランチが例えばmasterなら、あなたは単にgit checkout 1.0.0bin -- VeryBigBinary.exeを使えます。
  2. 孤児タグ1.0.0binをダウンロードしていない場合、これは失敗します。その場合は、事前にgit fetch <remote> 1.0.0binを実行する必要があります。
  3. あなたのマスターのVeryBigBinary.exe.gitignoreを追加することができます。そうすればあなたのチームの誰もが偶然にバイナリでプロジェクトの主な歴史を汚染することはありません。

バイナリファイルを完全に削除する

ローカルリポジトリ、リモートリポジトリ、および同僚のリポジトリからVeryBigBinary.exeを完全に削除することにした場合、次の操作を実行できます。

  1. リモートのgit Push <remote> :refs/tags/1.0.0binのOrphanタグを削除します
  2. Orphanタグをローカルに削除します(他のすべての参照されていないタグを削除します)git tag -l | xargs git tag -d && git fetch --tagsSO 1841341 から若干変更を加えたものです。
  3. あなたの今参照されていないコミットをローカルで削除するにはgit gcトリックを使ってください。 git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 -c gc.rerereresolved=0 -c gc.rerereunresolved=0 -c gc.pruneExpire=now gc "$@"。他の未参照のコミットもすべて削除されます。 SO 1904860から取得
  4. 可能であれば、リモコンでgit gcのトリックを繰り返してください。リポジトリをセルフホスティングしていてgithubのようなgitプロバイダや企業環境によっては不可能な場合があります。 sshにリモートアクセスを許可しないプロバイダでホスティングしているのなら、それを許可してください。あなたのプロバイダのインフラストラクチャが彼ら自身の甘い時間にあなたの参照されていないコミットを片付けることは可能です。企業環境にいる場合は、週に1回程度、リモートからcronジョブのガベージコレクションを実行するようにITにアドバイスすることができます。同僚にgit clone -b master --single-branch <url>ではなく常にgit cloneを推奨するのであれば、帯域幅とストレージの観点から、チームが影響を受けるかどうかは関係ありません。
  5. 時代遅れの孤児タグを取り除きたいすべての同僚は、ステップ2〜3を適用するだけで済みます。
  6. その後、バイナリファイルの追加の手順1〜8を繰り返して、新しい孤児タグ2.0.0binを作成できます。同僚がgit fetch <remote> --tagsと入力するのが心配な場合は、実際にもう一度1.0.0binと名前を付けることができます。これにより、次回それらがすべてのタグを取得したときに、古い1.0.0binが参照されず、後続のガベージコレクションのためにマークされます(手順3を使用)。あなたがリモート上のタグを上書きしようとするとき、あなたはこのように-fを使わなければなりません:git Push -f <remote> <tagname>

あとがき

  • OTABSはあなたのマスターや他のソースコード/開発ブランチには触れません。コミットハッシュ、すべての履歴、およびこれらのブランチの小さいサイズは影響を受けません。バイナリファイルを使ってソースコード履歴を既に肥大化させている場合は、それを別の作業としてクリーンアップする必要があります。 このスクリプト は役に立ちます。

  • Git-bashを使ってWindowsで動作することを確認しました。

  • バイナリファイルの格納をより効率的にするために、 一連の標準的なトリック を適用することをお勧めします。 git gcを(追加の引数なしで)頻繁に実行すると、gitはバイナリデルタを使用してファイルの基礎となるストレージを最適化します。しかし、ファイルがコミットからコミットへと変わらないと思われる場合は、バイナリデルタを完全に無効にすることができます。さらに、.zip、.jpg、または.cryptのようにすでに圧縮または暗号化されたファイルを圧縮することは意味がないので、gitでは基礎となるストレージの圧縮を無効にすることができます。残念ながら、これはあなたのソースコードにも影響を与えるオールオアナッシングの設定です。

  • あなたはより速い使用を可能にするためにOTABSの一部を台本にしたいと思うかもしれません。特に、バイナリファイルの完全削除からupdate gitフックへのスクリプトステップ2-3は、git fetch( "時代遅れのものすべてをフェッチして削除する"という説得力があるが恐らく危険なセマンティクスを与えるかもしれません。 ")。

  • あなたはバイナリファイルを完全に削除するのステップ4を飛ばしたくなるかもしれません。ローカルリポジトリは、時間の経過とともにスリムに保たれます。

  • Javaの世界では、このソリューションとmaven --offlineを組み合わせて、完全にあなたのバージョン管理に格納された再現可能なオフラインビルドを作成することができます(gradleよりもmavenの方が簡単です)。 Golangの世界では、go getの代わりにこのソリューションに基づいてGOPATHを管理することが可能です。 Pythonの世界では、これをvirtualenvと組み合わせることで、ビルドごとにPyPiサーバに頼らずに自己完結型の開発環境を一から作成することができます。

  • ビルド成果物のようにバイナリーファイルが頻繁に変更される場合は、最新バージョンの成果物の5つをOrphanタグmonday_bintuesday_bin、...、friday_bin、およびOrphanタグに格納するソリューションをスクリプト化することをお勧めします。リリースごとに1.7.8bin2.0.0binなど。weekday_binをローテーションしたり、古いバイナリを毎日削除したりできます。こうすることで、2つの長所を活用することができます。ソースコードの全体履歴を保持し、バイナリ依存関係のrelated履歴のみを保持します。与えられたタグのバイナリファイルをなしで取得するのもとても簡単です:全ての履歴を含むソースコード全体を取得する:git init && git remote add <name> <url> && git fetch <name> <tag>はあなたのためにそれをするべきです。

11
Adam Kurkiewicz

SVNはGitよりもバイナリデルタをより効率的に扱うようです。

ドキュメントのバージョン管理システム(JPEGファイル、PDFファイル、および.odtファイル)を決定する必要がありました。私はちょうどJPEGファイルを追加し、それを90度4回回転させてテストしました(バイナリデルタの有効性をチェックするため)。 Gitのリポジトリは400%成長しました。 SVNのリポジトリは11%しか成長していません。

そのため、SVNはバイナリファイルの方がはるかに効率的です。

だから私の選択はソースコードのGitとドキュメントのようなバイナリファイルのSVNです。

9
Tony Diep

私は、ソースコード(Webアプリケーション)が依存する大きなバイナリファイルを処理する方法についての意見を探しています。これに関するあなたの経験/考えは何ですか?

私は個人的にGitとの同期エラーに遭遇しました。Webアプリケーションのバイナリデータにノッチを付けた後、クラウドホストの一部でGBマークを超えるになりました。 BFT Repo Cleaner を検討しましたが、ハックのように感じました。それ以来、ファイルの管理、バージョン管理、バックアップのために、Amazon S3などの専用ツールを活用して、Gitの範囲外でファイルを保持し始めました。

複数のGitリポジトリを使用し、1つのプロジェクトでそれらを管理した経験がある人はいますか?

はい。 Hugoテーマ は、主にこの方法で管理されます。それは少し不器用ですが、仕事を終わらせます。


私の提案は、仕事に最適なツールを選択です。企業向けで、GitHubでコードラインを管理している場合は、お金を支払い、Git-LFSを使用します。そうでない場合は、分散型の暗号化された ブロックチェーンを使用したファイルストレージ など、より創造的なオプションを検討できます。

考慮すべき追加オプションには、 Minio および s3cmd があります。

2
Josh Habdas

Git 2.19以降のgit clone --filter +浅いクローン

GitとGitHubがそれを十分に使いやすくするならば、この新しいオプションは最終的にバイナリファイルの問題に対する最終的な解決策になるかもしれません(それらはおそらく間違いなく サブモジュール のためにまだ達成できません) 。

それは実際にあなたがサーバーのために欲しいファイルとディレクトリだけを取得することを可能にし、そしてリモートプロトコル拡張と一緒に導入されました。

これにより、最初に浅いクローンを作成してから、ビルドの種類ごとにビルドシステムでどのBLOBをフェッチするかを自動化できます。

フェッチする最大BLOBサイズを制限することを可能にする--filter=blob:limit<size>さえすでにあります。

この機能がどのように見えるかについての最小限の詳細な例を提供しました。 Gitリポジトリのみのサブディレクトリを複製するにはどうすればよいですか?