11

この質問は本質的にこれに似ていると前もって言います。これをユニークにする重要な違いが1つあります。生のgitプロトコルを使用したいです(基本的なパックネットワークプロトコルに慣れていない場合は、ここここを参照してください)。

匿名のgitリポジトリに接続するScalaとJGitを使用してアプリケーションを作成しています。単一のblobをリクエストしたい( "/path/to/file.txt" @ "refs / heads / branch1"と考えてください)。最終的に私の目標は、リモートリポジトリから単一のファイルをプログラムで取得することです。できることはかなり便利なことのようです。

とにかく、私はこのプロトコルの内部を掘り下げてきました。これの基本的なバージョンは「これらのオブジェクトが欲しい、私はこれらのオブジェクトを持っている」であるように見えます-そして、bam、あなたが持っていないすべてのものを含むパックファイルがあります。私の質問の核心はこれです:非再帰的な方法で単一のオブジェクトをgit-upload-packfileに要求するにはどうすればよいですか?単一のコミットオブジェクトをダウンロードしてから、ツリー、サブツリー、別のサブツリー、最後にブロブ自体を要求しても問題ありません。ここでは速度はそれほど重要ではありません。主に帯域幅を節約しようとしています。しかし、git-upload-packfileに「私が要求したオブジェクトを1つだけ与えてください」と伝える方法はないようです。

はい、「持っている」リストがあります。これは基本的にオブジェクトがダウンするのを除外しますが、リポジトリの内容について事前に知っている必要があります(ローカルリポジトリはありません、覚えておいてください)。考えられるすべてのsha1のリストを生成し、必要なものを除いてすべてを送信することはできますが、それはばかげたことを超えています(時間と帯域幅を消費し、あらゆる場所のプログラマーに対する犯罪)

私が調べてきたもう1つの可能な解決策は、代わりにリモート側でgit-upload-archiveを使用することですが、まだ調査に多くの時間を費やしていないことは認めます。

それに関しては、私はJGitを書き直したいと思っているので、これを「JGitをどのように実行させるか...」と読まないでください。プロトコル自体がこれに対応できるかどうかを知りたいだけです。私が望むものを達成するためにプロトコルを悪用するいくつかの素晴らしく賢い方法があるように私は感じます。何かご意見は?

4

1 に答える 1

13

私自身の質問に答えます。私は受け入れられる(ほとんど文書化されていませんが)答えを見つけました。私はこれを理解するためにたくさんのCコードを掘り下げなければなりませんでした。

まず第一に、上記の要件はgit-upload-packfile、プログラムが実行するように設計されたものではないため、を使用して達成することはできません。私が疑った正解はですgit-upload-archive。残念ながら、プロトコルはほとんど文書化されていません。それで、他の誰かが同様の要件を持っている場合に備えて、これについての私のメモがあります。

基本的に、ここで(scalaで)シミュレートしようとしているのは、次のコマンドです。

git archive --format=tar --remote=ssh://dave@ssh.mycompany.com/cornballer.git \
  > master plans/documents/cornballer-blueprint.pdf | tar -x

ソフトウェアを除いて、うまくいけばJGitを使用します。残念ながら、JGitは(まだ)gitアーカイブコマンドをサポートしていません。したがって、サポートを追加する方法の非常に高レベルの概要を次に示します(JGitをフォークして、後で追加する場合があります)。

プロトコルを見てみましょう(Documentation / technology / pack-protocol.txtから):

git-proto-request = request-command SP pathname NUL [ host-parameter NUL ]
request-command   = "git-upload-pack" / "git-receive-pack" /
                    "git-upload-archive"   ; case sensitive
pathname          = *( %x01-ff ) ; exclude NUL
host-parameter    = "host=" hostname [ ":" port ]

したがって、プロトコルのパート1は次のようになります。

  1. リモートでトランスポートを確立します(sshを実行git-upload-archiveしてから、匿名gitプロトコルを実行または使用します)
  2. 送信git-upload-archive /cornballer.git\0host=ssh.mycompany.com\0(パケットラインとして)

この時点で接続が確立されます。Gコマンドがサポートされていない場合、または何らかの問題が発生した場合は、エラーが返されることがあります。これを確認する方法はまだわかりません。

次は、文書化されていない部分です。基本的に、コマンドライン引数をネットワーク経由で送信しますgit-archivegit-archiveこれらは、1つの例外を除いて、コマンドとまったく同じです。すべてのプレフィックスに。が付いていargument[SPACE]ます。各引数は(少なくともリファレンス実装では)個別のパケットラインとして記述されます。したがって、上記の例では、次のようになります。

  1. 送信argument --format=tar(パケットラインとして)
  2. 送信argument master(パケットラインとして)
  3. 送信argument plans/documents/cornballer-blueprint.pdf(パケットラインとして)
  4. フラッシュパケットを送信する(0000

この時点で、リモートgit-archiveプロセスにコマンド全体を与えました。次に、応答を読みます。サーバーから1つのパケット行を読み取ります。これは、次のいずれかの応答になります。

  1. ACK(成功を意味します-アーカイブを送信する準備ができています)
  2. NACK [message]-ある種のエラー、その使用のインスタンスが1つだけ見つかりました-「サブプロセスを生成できません」
  3. ERR [message]- エラーが発生しました

ACK送信されると、その後にフラッシュパケット(0000)が続き、次に生のtarデータが続きます。この時点で、側波帯#1(メインデータチャネル)に着信するパケットラインを繰り返し読み取ります。フラッシュパケットに到達すると、読み取りを停止します。ものすごく単純。

これでリモートファイルができましたが、ある種の巧妙なキャッシュを実行したい場合はどうでしょうか。私が非常に熱心に使用していた理由の1つgit-upload-packfileは、コミットIDを記録してローカルにキャッシュし、必要な場合にのみ更新できるようにするためです。tarファイルはその情報を教えてくれませんか?間違い!

git-archiveのmanページから:

さらに、tar形式が使用されている場合、コミットIDはグローバル拡張paxヘッダーに格納されます。gitget-tar-commit-idを使用して抽出できます。ZIPファイルでは、ファイルコメントとして保存されます。

それは素晴らしいニュースです!それは文字通り私が欲しかったすべてです。ヘッダーがどのように見えるか疑問に思われる場合は、サンプルを次に示します(paxヘッダーを分析するつもりはありません)。

pax_global_header00006660000000000000000000000064121002672560014513gustar00rootroot0000000000000052 comment=326756f834865880c9832b64238e7665632e9b67

したがって、私の観点からは、上記のステップを自動的に実行するパイプラインを設定し、untarステップを(プログラムで)実行して、目的の「gitから単一ファイルをフェッチする」機能を実行する必要があります。

于 2013-01-30T17:47:57.677 に答える