日本語 ▾ トピック ▾ 最新バージョン ▾ gitprotocol-pack は 2.43.0 で最終更新されました

名前

gitprotocol-pack - パックがネットワーク経由で転送される方法

概要

<over-the-wire-protocol>

説明

Git は、ssh://、git://、http://、および file:// トランスポートを介してパックファイル内のデータを転送することをサポートしています。クライアントからサーバーにデータをプッシュするための一連のプロトコルと、サーバーからクライアントにデータをフェッチするための一連のプロトコルがあります。3 つのトランスポート (ssh、git、file) は、同じプロトコルを使用してデータを転送します。http は gitprotocol-http[5] に記述されています。

正準 Git 実装で呼び出されるプロセスは、データをフェッチする場合、サーバー側で _upload-pack_、クライアント側で _fetch-pack_ です。データをプッシュする場合、サーバー側で _receive-pack_、クライアント側で _send-pack_ です。プロトコルは、サーバーがクライアントに現在サーバーにあるものを伝え、次に両者が互いを完全に更新するために送信するデータの最小量をネゴシエートするように機能します。

pkt-line フォーマット

以下の説明は、gitprotocol-common[5] に記述されている pkt-line フォーマットに基づいています。文法が PKT-LINE(...) を示す場合、特に指定のない限り、通常の pkt-line LF ルールが適用されます。送信者は LF を含めるべきですが (SHOULD)、受信者は LF が存在しなくても苦情を言ってはなりません (MUST NOT)。

エラーパケットは、エラー文字列を含む特殊な pkt-line です。

  error-line     =  PKT-LINE("ERR" SP explanation-text)

プロトコル全体で、PKT-LINE(...) が期待される箇所で、エラーパケットが送信されることがあります (MAY)。このパケットがクライアントまたはサーバーによって送信されると、このプロトコルで定義されているデータ転送プロセスは終了します。

トランスポート

パックファイルプロトコルが開始されるトランスポートは 3 つあります。Git トランスポートは、シンプルで認証されていないサーバーで、クライアントが通信したいコマンド (ほぼ常に _upload-pack_ ですが、Git サーバーはグローバルに書き込み可能に設定することもでき、その場合 _receive-pack_ の開始も許可されます) を受け取り、それを実行し、要求するプロセスに接続します。

SSH トランスポートでは、クライアントは SSH プロトコルを介してサーバー上で _upload-pack_ または _receive-pack_ プロセスを実行し、SSH 接続を介して呼び出されたプロセスと通信します。

file:// トランスポートは、_upload-pack_ または _receive-pack_ プロセスをローカルで実行し、パイプを介してそれと通信します。

追加パラメータ

このプロトコルは、クライアントが最初のメッセージでサーバーに追加情報を送信できるメカニズムを提供します。これらは「追加パラメータ」と呼ばれ、Git、SSH、HTTP プロトコルでサポートされています。

各追加パラメータは <key>=<value> または <key> の形式をとります。

このような追加パラメータを受け取るサーバーは、認識しないすべてのキーを無視しなければなりません (MUST)。現在、認識されている唯一の追加パラメータは、値が _1_ または _2_ の「version」です。プロトコルバージョン 2 の詳細については、gitprotocol-v2[5] を参照してください。

Git トランスポート

Git トランスポートは、まず pkt-line フォーマットを使用してコマンドとリポジトリをネットワーク経由で送信し、その後に NULL バイトとホスト名パラメータを続け、NULL バイトで終了します。

0033git-upload-pack /project.git\0host=myserver.com\0

トランスポートは、追加の NULL バイトを追加し、その後に 1 つ以上の NULL 終端文字列を追加することで、追加パラメータを送信できます。

003egit-upload-pack /project.git\0host=myserver.com\0\0version=1\0
git-proto-request = request-command SP pathname NUL
      [ host-parameter NUL ] [ NUL extra-parameters ]
request-command   = "git-upload-pack" / "git-receive-pack" /
      "git-upload-archive"   ; case sensitive
pathname          = *( %x01-ff ) ; exclude NUL
host-parameter    = "host=" hostname [ ":" port ]
extra-parameters  = 1*extra-parameter
extra-parameter   = 1*( %x01-ff ) NUL

ホストパラメータは、git-daemon の名前ベースの仮想ホスティングに使用されます。git daemon の --interpolated-path オプションと、%H/%CH フォーマット文字を参照してください。

基本的に、Git クライアントが Git プロトコルを介してサーバー側の _upload-pack_ プロセスに接続するために行っていることは次のとおりです。

$ echo -e -n \
  "003agit-upload-pack /schacon/gitbook.git\0host=example.com\0" |
  nc -v example.com 9418

SSH トランスポート

SSH を介して upload-pack または receive-pack プロセスを開始することは、SSH リモート実行を介してサーバー上でバイナリを実行することです。基本的に、これは次を実行するのと同じです。

$ ssh git.example.com "git-upload-pack '/project.git'"

サーバーが SSH 経由で特定のユーザーの Git プッシュとプルをサポートするには、そのユーザーがログイン時に提供される SSH シェルを介して、それらのコマンドのいずれかまたは両方を実行できる必要があります。一部のシステムでは、シェルアクセスがそれら 2 つのコマンド、またはどちらか一方のみを実行できるように制限されています。

ssh:// 形式の URI では、URI 内で絶対パスになるため、ホスト名 (またはポート番号) の後の _/_ が引数として送信され、リモートの git-upload-pack によってそのまま読み取られ、結果的にリモートファイルシステム内の絶対パスとなります。

   git clone ssh://user@example.com/project.git
  |
  v
ssh user@example.com "git-upload-pack '/project.git'"

"user@host:path" 形式の URI では、ユーザーのホームディレクトリからの相対パスになります。なぜなら、Git クライアントは以下を実行するからです。

   git clone user@example.com:project.git
    |
    v
ssh user@example.com "git-upload-pack 'project.git'"

例外は _~_ が使用される場合で、その場合は先頭の _/_ なしで実行されます。

   ssh://user@example.com/~alice/project.git,
    |
    v
ssh user@example.com "git-upload-pack '~alice/project.git'"

protocol.version 設定変数の値に応じて、Git は GIT_PROTOCOL 環境変数にコロン区切りの文字列として追加パラメータを送信しようとする場合があります。これは、ssh.variant 設定変数が ssh コマンドが環境変数を引数として渡すことをサポートしていることを示している場合にのみ行われます。

ここでいくつか覚えておくべきこと

  • "コマンド名"はダッシュで表記されます (例: git-upload-pack) が、これはクライアントによって上書きできます。

  • リポジトリパスは常に単一引用符で囲まれています。

サーバーからデータをフェッチする

ある Git リポジトリが別のリポジトリにあるデータを取得したい場合、最初の Git リポジトリは 2 番目のリポジトリから _fetch_ することができます。この操作は、サーバーが持っていてクライアントが持っていないデータを特定し、そのデータをパックファイル形式でクライアントにストリーミングします。

参照発見

クライアントが最初に接続すると、サーバーはすぐにバージョン番号 (「version=1」が追加パラメータとして送信された場合) と、各参照 (すべてのブランチとタグ) が現在指しているオブジェクト名とともに、サーバーが持つ各参照のリストで応答します。

 $ echo -e -n "0045git-upload-pack /schacon/gitbook.git\0host=example.com\0\0version=1\0" |
    nc -v example.com 9418
 000eversion 1
 00887217a7c7e582c46cec22a130adf4b9d7d950fba0 HEAD\0multi_ack thin-pack
side-band side-band-64k ofs-delta shallow no-progress include-tag
 00441d3fcd5ced445d1abc402225c0b8a1299641f497 refs/heads/integration
 003f7217a7c7e582c46cec22a130adf4b9d7d950fba0 refs/heads/master
 003cb88d2441cac0977faf98efc80305012112238d9d refs/tags/v0.9
 003c525128480b96c89e6418b1e40909bf6c5b2d580f refs/tags/v1.0
 003fe92df48743b7bc7d26bcaabfddde0a1e20cae47c refs/tags/v1.0^{}
 0000

返される応答は、各参照とその現在の値を記述する pkt-line ストリームです。ストリームは、C ロケールの順序に従って名前でソートされなければなりません (MUST)。

HEAD が有効な参照である場合、HEAD は最初にアドバタイズされた参照として表示されなければなりません (MUST)。HEAD が有効な参照でない場合、HEAD はアドバタイズリストにまったく表示されてはなりません (MUST NOT) が、他の参照は引き続き表示されることがあります。

ストリームは、最初の参照の NUL の後ろに機能宣言を含まなければなりません (MUST)。参照の剥がされた値 (つまり「ref^{}」) は、提示された場合、参照自体の直後になければなりません (MUST)。適合するサーバーは、注釈付きタグである場合、参照を剥がさなければなりません (MUST)。

  advertised-refs  =  *1("version 1")
		      (no-refs / list-of-refs)
		      *shallow
		      flush-pkt

  no-refs          =  PKT-LINE(zero-id SP "capabilities^{}"
		      NUL capability-list)

  list-of-refs     =  first-ref *other-ref
  first-ref        =  PKT-LINE(obj-id SP refname
		      NUL capability-list)

  other-ref        =  PKT-LINE(other-tip / other-peeled)
  other-tip        =  obj-id SP refname
  other-peeled     =  obj-id SP refname "^{}"

  shallow          =  PKT-LINE("shallow" SP obj-id)

  capability-list  =  capability *(SP capability)
  capability       =  1*(LC_ALPHA / DIGIT / "-" / "_")
  LC_ALPHA         =  %x61-7A

サーバーとクライアントは obj-id に小文字を使用しなければならず (MUST)、両者は obj-id を大文字と小文字を区別しないものとして扱わなければなりません (MUST)。

許可されたサーバー機能と説明のリストについては、protocol-capabilities.txt を参照してください。

パックファイルのネゴシエーション

参照と機能の発見後、クライアントは、パックデータが不要な場合に、flush-pkt を送信して接続を終了し、サーバーに正常に終了して切断できることを伝えることができます。これは ls-remote コマンドで発生する可能性があり、クライアントがすでに最新である場合にも発生する可能性があります。

そうでない場合は、ネゴシエーションフェーズに入ります。クライアントとサーバーは、必要な最小限のパックファイルを転送するために、サーバーに要求するオブジェクト、シャローオブジェクト (もしあれば)、および要求するコミット履歴の最大深度 (もしあれば) を伝えます。クライアントは、サーバーが最初の _want_ 行で実行できると述べた機能の中から、有効にしたい機能のリストも送信します。

  upload-request    =  want-list
		       *shallow-line
		       *1depth-request
		       [filter-request]
		       flush-pkt

  want-list         =  first-want
		       *additional-want

  shallow-line      =  PKT-LINE("shallow" SP obj-id)

  depth-request     =  PKT-LINE("deepen" SP depth) /
		       PKT-LINE("deepen-since" SP timestamp) /
		       PKT-LINE("deepen-not" SP ref)

  first-want        =  PKT-LINE("want" SP obj-id SP capability-list)
  additional-want   =  PKT-LINE("want" SP obj-id)

  depth             =  1*DIGIT

  filter-request    =  PKT-LINE("filter" SP filter-spec)

クライアントは、参照発見フェーズで要求するすべての obj-id を _want_ 行として送信しなければなりません (MUST)。クライアントは、リクエストボディに少なくとも 1 つの _want_ コマンドを送信しなければなりません (MUST)。クライアントは、参照発見によって取得された応答に表示されなかった obj-id を _want_ コマンドで言及してはなりません (MUST NOT)。

クライアントは、浅いコピー (コミットの親がないことを意味します) のみを持つすべての obj-id を _shallow_ 行として書き込む必要があります。これにより、サーバーはクライアントの履歴の制限を認識できます。

クライアントは、このトランザクションで必要な最大コミット履歴深度を _deepen_ 行として送信します。これは、履歴の先端から必要なコミットの数です (もしあれば)。深度 0 は深度要求を行わないことと同じです。クライアントは、この深度を超えるコミットを受け取りたくありませんし、それらのコミットを完了するためにのみ必要なオブジェクトも受け取りたくありません。結果として親が受け取られないコミットは、シャローとして定義され、サーバーでそのようにマークされます。この情報は次のステップでクライアントに返送されます。

クライアントは、いくつかのフィルタリング技術のいずれかを使用して、パックファイルから様々なオブジェクトを除外するようにパックオブジェクトにオプションで要求できます。これらは、部分クローンおよび部分フェッチ操作で使用することを目的としています。filter-spec の値に合致しないオブジェクトは、_want_ 行で明示的に要求されない限り、省略されます。可能な filter-spec の値については、rev-list を参照してください。

すべての _want_ と _shallow_ (およびオプションの _deepen_) が転送されたら、クライアントは flush-pkt を送信して、リストの送信が完了したことをサーバー側に伝えなければなりません (MUST)。

そうでなければ、クライアントが正の深度要求を送信した場合、サーバーはシャローになるコミットとそうでないコミットを決定し、この情報をクライアントに送信します。クライアントが正の深度を要求しなかった場合、このステップはスキップされます。

  shallow-update   =  *shallow-line
		      *unshallow-line
		      flush-pkt

  shallow-line     =  PKT-LINE("shallow" SP obj-id)

  unshallow-line   =  PKT-LINE("unshallow" SP obj-id)

クライアントが正の深度を要求した場合、サーバーは、要求された深度よりも深くならないコミットのセットを計算します。コミットのセットは、クライアントの要望から始まります。

サーバーは、親が結果として送信されない各コミットに対して _shallow_ 行を書き込みます。サーバーは、クライアントがシャローであることを示したが、現在要求された深度ではシャローではなくなった (つまり、親が送信されるようになった) 各コミットに対して _unshallow_ 行を書き込みます。サーバーは、クライアントがシャローであることを示していないものをアンシャローとしてマークしてはなりません (MUST NOT)。

これでクライアントは、持っている obj-id のリストを _have_ 行を使って送信します。これにより、サーバーはクライアントが必要とするオブジェクトのみを含むパックファイルを作成できます。multi_ack モードでは、正準な実装は一度に最大 32 個のこれらの情報を送信し、その後 flush-pkt を送信します。正準な実装はすぐに次に進み、次の 32 個をすぐに送信するため、常に一度に 32 個の「ネットワーク上を飛行中のブロック」が存在することになります。

  upload-haves      =  have-list
		       compute-end

  have-list         =  *have-line
  have-line         =  PKT-LINE("have" SP obj-id)
  compute-end       =  flush-pkt / PKT-LINE("done")

サーバーが _have_ 行を読み取ると、クライアントが持っていると述べ、かつサーバーも持っている obj-id のいずれかを ACK することで応答します。サーバーは、クライアントが選択した ACK モードに応じて、obj-id を異なる方法で ACK します。

multi_ack モードでは

  • サーバーは、共通のコミットに対して _ACK obj-id continue_ で応答します。

  • サーバーが許容できる共通のベースコミットを見つけ、パックファイルを作成する準備ができたら、すべての _have_ obj-id をクライアントに盲目的に ACK します。

  • その後、サーバーは _NAK_ を送信し、クライアントからの別の応答 (_done_ または別の _have_ 行のリスト) を待ちます。

multi_ack_detailed モードでは

  • サーバーは、データを送信する準備ができていることを示す _ACK obj-id ready_ 行と、識別された共通コミットを示す _ACK obj-id common_ 行とを区別して ACK を行います。

multi_ack または multi_ack_detailed のいずれも使用しない場合

  • upload-pack は、最初に見つかった共通オブジェクトに対して「ACK obj-id」を送信します。その後、クライアントが「done」を送信するまで何も言いません。

  • upload-pack は、共通オブジェクトがまだ見つかっていない場合、flush-pkt で「NAK」を送信します。共通オブジェクトが見つかっていて、すでに ACK が送信されている場合は、flush-pkt では沈黙します。

クライアントが十分な ACK 応答を受け取り、効率的なパックファイルを送信するためにサーバーが十分な情報を持っていると判断したとき (正準な実装では、--date-order キューに残っているすべてをサーバーと共通であると色付けできる十分な ACK を受信したとき、または --date-order キューが空のときにこれが決定されます)、またはクライアントがあきらめることを決定したとき (正準な実装では、クライアントがサーバーから ACK されない 256 の _have_ 行を送信したとき、つまり共通点がなく、サーバーはすべてのオブジェクトを送信すべきであると判断されたときにこれが決定されます)、クライアントは _done_ コマンドを送信します。_done_ コマンドは、クライアントがパックファイルデータを受信する準備ができていることをサーバーに通知します。

ただし、正準なクライアント実装では、以前のラウンドで少なくとも1つの「ACK %s continue」を受信した場合にのみ、256の制限が有効になります。これにより、完全に諦める前に、少なくとも1つの共通祖先が見つかることが保証されます。

クライアントから _done_ 行が読み取られると、サーバーは最終的な _ACK obj-id_ を送信するか、_NAK_ を送信します。_obj-id_ は、共通であると判断された最後のコミットのオブジェクト名です。サーバーは、共通ベースが少なくとも1つ存在し、multi_ack または multi_ack_detailed が有効になっている場合にのみ、_done_ の後に ACK を送信します。サーバーは、共通ベースが見つからない場合、常に _done_ の後に NAK を送信します。

_ACK_ または _NAK_ の代わりに、サーバーはエラーメッセージを送信する場合があります (たとえば、クライアントから受信した _want_ 行のオブジェクトを認識しない場合など)。

その後、サーバーはパックファイルデータの送信を開始します。

  server-response = *ack_multi ack / nak
  ack_multi       = PKT-LINE("ACK" SP obj-id ack_status)
  ack_status      = "continue" / "common" / "ready"
  ack             = PKT-LINE("ACK" SP obj-id)
  nak             = PKT-LINE("NAK")

簡単なクローンは次のようになります (_have_ 行なし)

   C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \
     side-band-64k ofs-delta\n
   C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n
   C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n
   C: 0032want 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n
   C: 0032want 74730d410fcb6603ace96f1dc55ea6196122532d\n
   C: 0000
   C: 0009done\n

   S: 0008NAK\n
   S: [PACKFILE]

増分更新 (フェッチ) の応答は次のようになるかもしれません。

   C: 0054want 74730d410fcb6603ace96f1dc55ea6196122532d multi_ack \
     side-band-64k ofs-delta\n
   C: 0032want 7d1665144a3a975c05f1f43902ddaf084e784dbe\n
   C: 0032want 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a\n
   C: 0000
   C: 0032have 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01\n
   C: [30 more have lines]
   C: 0032have 74730d410fcb6603ace96f1dc55ea6196122532d\n
   C: 0000

   S: 003aACK 7e47fe2bd8d01d481f44d7af0531bd93d3b21c01 continue\n
   S: 003aACK 74730d410fcb6603ace96f1dc55ea6196122532d continue\n
   S: 0008NAK\n

   C: 0009done\n

   S: 0031ACK 74730d410fcb6603ace96f1dc55ea6196122532d\n
   S: [PACKFILE]

パックファイルデータ

クライアントとサーバーが、クライアントに送信する必要がある最小限のデータについてネゴシエーションを終えたので、サーバーは必要なデータをパックファイル形式で構築して送信します。

パックファイル自体が実際にどのように見えるかについては、gitformat-pack[5] を参照してください。

クライアントによって _side-band_ または _side-band-64k_ 機能が指定されている場合、サーバーはパックファイルデータを多重化して送信します。

各パケットは、続くデータ量のパケットライン長で始まり、その後に続くデータがどのサイドバンドで送信されているかを指定する単一バイトが続きます。

_side-band_ モードでは、最大 999 バイトのデータと 1 つの制御コードを送信し、pkt-line で合計最大 1000 バイトになります。_side-band-64k_ モードでは、最大 65519 バイトのデータと 1 つの制御コードを送信し、pkt-line で合計最大 65520 バイトになります。

サイドバンドバイトは _1_、_2_、または _3_ になります。サイドバンド _1_ にはパックファイルデータが含まれ、サイドバンド _2_ はクライアントが通常 stderr に出力する進捗情報に使用され、サイドバンド _3_ はエラー情報に使用されます。

_side-band_ 機能が指定されていない場合、サーバーは多重化せずにパックファイル全体をストリーミングします。

サーバーにデータをプッシュする

サーバーへのデータプッシュは、サーバー上で _receive-pack_ プロセスを呼び出します。これにより、クライアントは更新すべき参照をサーバーに伝え、サーバーがこれらの新しい参照を完了するために必要とするすべてのデータを送信します。すべてのデータが受信され検証されると、サーバーはクライアントが指定した参照に更新します。

認証

プロトコル自体には認証メカニズムは含まれていません。それは _receive-pack_ プロセスが呼び出される前に、SSH のようなトランスポートによって処理されます。Git トランスポート経由で _receive-pack_ が設定されている場合、そのトランスポートは認証されていないため、そのポート (9418) にアクセスできるすべての人がそのリポジトリに書き込めることになります。

参照発見

参照発見フェーズは、フェッチプロトコルとほぼ同じ方法で行われます。サーバー上の各参照の obj-id と名前がパケットライン形式でクライアントに送信され、その後 flush-pkt が続きます。唯一の本当の違いは、機能リストが異なることです。可能な値は _report-status_、_report-status-v2_、_delete-refs_、_ofs-delta_、_atomic_、_push-options_ のみです。

参照更新要求とパックファイル転送

クライアントは、サーバーがどの参照にあるかを知ると、参照更新リクエストのリストを送信できます。更新したいサーバー上の各参照について、現在サーバー上にある obj-id、クライアントが更新したい obj-id、および参照の名前を記載した行を送信します。

このリストの後には flush-pkt が続きます。

  update-requests   =  *shallow ( command-list | push-cert )

  shallow           =  PKT-LINE("shallow" SP obj-id)

  command-list      =  PKT-LINE(command NUL capability-list)
		       *PKT-LINE(command)
		       flush-pkt

  command           =  create / delete / update
  create            =  zero-id SP new-id  SP name
  delete            =  old-id  SP zero-id SP name
  update            =  old-id  SP new-id  SP name

  old-id            =  obj-id
  new-id            =  obj-id

  push-cert         = PKT-LINE("push-cert" NUL capability-list LF)
		      PKT-LINE("certificate version 0.1" LF)
		      PKT-LINE("pusher" SP ident LF)
		      PKT-LINE("pushee" SP url LF)
		      PKT-LINE("nonce" SP nonce LF)
		      *PKT-LINE("push-option" SP push-option LF)
		      PKT-LINE(LF)
		      *PKT-LINE(command LF)
		      *PKT-LINE(gpg-signature-lines LF)
		      PKT-LINE("push-cert-end" LF)

  push-option       =  1*( VCHAR | SP )

サーバーが _push-options_ 機能を通知し、クライアントが上記の機能リストの一部として _push-options_ を指定した場合、クライアントはプッシュオプションを送信し、その後 flush-pkt を送信します。

  push-options      =  *PKT-LINE(push-option) flush-pkt

古い Git サーバーとの下位互換性のために、クライアントがプッシュ証明書とプッシュオプションを送信する場合、プッシュオプションはプッシュ証明書に埋め込まれた形と、プッシュ証明書の後ろの両方で送信しなければなりません (MUST)。(証明書内のプッシュオプションにはプレフィックスが付いていますが、証明書後のプッシュオプションにはプレフィックスが付いていないことに注意してください。) これら両方のリストは、プレフィックスを除けば同じでなければなりません (MUST)。

その後、サーバーが新しい参照を完了するために必要とするすべてのオブジェクトを含むパックファイルが送信されます。

  packfile          =  "PACK" 28*(OCTET)

受信側が delete-refs をサポートしていない場合、送信側は delete コマンドを要求してはなりません (MUST NOT)。

受信側が push-cert をサポートしていない場合、送信側は push-cert コマンドを送信してはなりません (MUST NOT)。push-cert コマンドが送信された場合、command-list は送信してはなりません (MUST NOT)。代わりにプッシュ証明書に記録されたコマンドが使用されます。

使用されるコマンドが _delete_ のみの場合、パックファイルは送信してはなりません (MUST NOT)。

create または update コマンドが使用された場合、サーバーが既に必要なすべてのオブジェクトを持っていても、パックファイルは送信されなければなりません (MUST)。この場合、クライアントは空のパックファイルを送信しなければなりません (MUST)。これが起こる唯一の可能性は、クライアントが既存の obj-id を指す新しいブランチまたはタグを作成している場合です。

サーバーはパックファイルを受信し、それを解凍し、要求が処理されている間に変更されていない (obj-id が依然として old-id と同じである) 更新される各参照を検証し、更新が許容可能であることを確認するために任意の更新フックを実行します。これらすべてが問題なければ、サーバーは参照を更新します。

プッシュ証明書

プッシュ証明書は一連のヘッダー行で始まります。ヘッダーと空行の後、プロトコルコマンドが1行ずつ続きます。プッシュ証明書の PKT-LINE の末尾の LF はオプションでは _なく_、存在しなければならないことに注意してください。

現在、以下のヘッダーフィールドが定義されています。

pusher ident

GPG キーを「Human Readable Name 」形式で識別します。

pushee url

git push を実行したユーザーがプッシュしようとしたリポジトリの URL (URL に認証情報が含まれている場合は匿名化されます)。

nonce nonce

リプレイ攻撃を防ぐため、受信リポジトリがプッシュするユーザーに証明書に含めるよう要求した _nonce_ 文字列。

GPG 署名行は、署名ブロックが始まる前にプッシュ証明書に記録された内容に対する分離署名です。分離署名は、コマンドがプッシャー (署名者でなければならない) によって与えられたことを証明するために使用されます。

ステータスの報告

送信者からパックデータを受信した後、_report-status_ または _report-status-v2_ 機能が有効な場合、受信者はレポートを送信します。それはその更新で何が起こったかの短いリストです。まず、パックファイルの解凍ステータスを _unpack ok_ または _unpack [error]_ のいずれかでリストします。次に、更新を試みた各参照のステータスをリストします。各行は、更新が成功した場合は _ok [refname]_、更新が失敗した場合は _ng [refname] [error]_ となります。

  report-status     = unpack-status
		      1*(command-status)
		      flush-pkt

  unpack-status     = PKT-LINE("unpack" SP unpack-result)
  unpack-result     = "ok" / error-msg

  command-status    = command-ok / command-fail
  command-ok        = PKT-LINE("ok" SP refname)
  command-fail      = PKT-LINE("ng" SP refname SP error-msg)

  error-msg         = 1*(OCTET) ; where not "ok"

_report-status-v2_ 機能は、_proc-receive_ フックによって書き換えられた参照のレポートをサポートするために、新しいオプション行を追加することによってプロトコルを拡張します。_proc-receive_ フックは、1つ以上の参照を作成または更新する擬似参照のコマンドを処理することができ、各参照は異なる名前、異なる新しいoid、異なる古いoidを持つことができます。

  report-status-v2  = unpack-status
		      1*(command-status-v2)
		      flush-pkt

  unpack-status     = PKT-LINE("unpack" SP unpack-result)
  unpack-result     = "ok" / error-msg

  command-status-v2 = command-ok-v2 / command-fail
  command-ok-v2     = command-ok
		      *option-line

  command-ok        = PKT-LINE("ok" SP refname)
  command-fail      = PKT-LINE("ng" SP refname SP error-msg)

  error-msg         = 1*(OCTET) ; where not "ok"

  option-line       = *1(option-refname)
		      *1(option-old-oid)
		      *1(option-new-oid)
		      *1(option-forced-update)

  option-refname    = PKT-LINE("option" SP "refname" SP refname)
  option-old-oid    = PKT-LINE("option" SP "old-oid" SP obj-id)
  option-new-oid    = PKT-LINE("option" SP "new-oid" SP obj-id)
  option-force      = PKT-LINE("option" SP "forced-update")

更新が成功しない理由はいくつかあります。参照が最初に参照発見フェーズが送信されて以来変更された可能性があり、これはその間に誰かがプッシュしたことを意味します。プッシュされる参照が fast-forward でない参照であり、更新フックまたは設定がそれを許可しないように設定されているなどです。また、一部の参照は更新できる一方で、他の参照は拒否されることもあります。

クライアント/サーバー通信の例は次のようになるかもしれません。

   S: 006274730d410fcb6603ace96f1dc55ea6196122532d refs/heads/local\0report-status delete-refs ofs-delta\n
   S: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe refs/heads/debug\n
   S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/master\n
   S: 003d74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/team\n
   S: 0000

   C: 00677d1665144a3a975c05f1f43902ddaf084e784dbe 74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/debug\n
   C: 006874730d410fcb6603ace96f1dc55ea6196122532d 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/master\n
   C: 0000
   C: [PACKDATA]

   S: 000eunpack ok\n
   S: 0018ok refs/heads/debug\n
   S: 002ang refs/heads/master non-fast-forward\n

GIT

git[1]スイートの一部

scroll-to-top