English ▾ Themen ▾ Neueste Version ▾ gitprotocol-pack zuletzt aktualisiert in 2.43.0

NAME

gitprotocol-pack - Wie Packs über das Netzwerk übertragen werden

SYNOPSIS

<over-the-wire-protocol>

BESCHREIBUNG

Git unterstützt die Übertragung von Daten in Packfiles über die ssh://, git://, http:// und file:// Transporte. Es gibt zwei Protokollsätze, einen für das Pushen von Daten von einem Client an einen Server und einen anderen für das Abrufen von Daten von einem Server an einen Client. Die drei Transporte (ssh, git, file) verwenden dasselbe Protokoll zur Datenübertragung. http wird in gitprotocol-http[5] dokumentiert.

Die in der kanonischen Git-Implementierung aufgerufenen Prozesse sind auf der Serverseite upload-pack und auf der Clientseite fetch-pack für den Datenabruf; dann receive-pack auf dem Server und send-pack auf dem Client für das Pushen von Daten. Das Protokoll dient dazu, dass ein Server einem Client mitteilt, was sich gerade auf dem Server befindet, und dann die kleinste Datenmenge aushandelt, die gesendet werden muss, um den einen oder anderen vollständig zu aktualisieren.

pkt-line Format

Die folgenden Beschreibungen bauen auf dem pkt-line-Format auf, das in gitprotocol-common[5] beschrieben wird. Wenn die Grammatik PKT-LINE(...) angibt, gelten, sofern nicht anders vermerkt, die üblichen pkt-line LF-Regeln: Der Sender SOLLTE ein LF enthalten, aber der Empfänger DARF sich nicht beschweren, wenn es nicht vorhanden ist.

Ein Fehlerpaket ist ein spezielles pkt-line, das eine Fehlerzeichenkette enthält.

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

Über das gesamte Protokoll hinweg, wo PKT-LINE(...) erwartet wird, KANN ein Fehlerpaket gesendet werden. Sobald dieses Paket von einem Client oder einem Server gesendet wurde, ist der in diesem Protokoll definierte Datenübertragungsprozess beendet.

Transporte

Es gibt drei Transporte, über die das Packfile-Protokoll initiiert wird. Der Git-Transport ist ein einfacher, unauthentifizierter Server, der den Befehl (fast immer upload-pack, obwohl Git-Server so konfiguriert werden können, dass sie global schreibbar sind, wobei die Initiierung von receive-pack ebenfalls zulässig ist) entgegennimmt, mit dem sich der Client verbinden möchte, ihn ausführt und ihn mit dem anfragenden Prozess verbindet.

Beim SSH-Transport führt der Client einfach den Prozess upload-pack oder receive-pack auf dem Server über das SSH-Protokoll aus und kommuniziert dann mit diesem aufgerufenen Prozess über die SSH-Verbindung.

Der file:// Transport führt den Prozess upload-pack oder receive-pack lokal aus und kommuniziert über eine Pipe mit ihm.

Zusätzliche Parameter

Das Protokoll bietet einen Mechanismus, mit dem Clients zusätzliche Informationen in ihrer ersten Nachricht an den Server senden können. Diese werden als "Extra-Parameter" bezeichnet und werden von den Git-, SSH- und HTTP-Protokollen unterstützt.

Jeder Extra-Parameter hat die Form <key>=<value> oder <key>.

Server, die solche Extra-Parameter empfangen, MÜSSEN alle nicht erkannten Schlüssel ignorieren. Derzeit ist der einzige erkannte Extra-Parameter "version" mit dem Wert 1 oder 2. Weitere Informationen zu Protokollversion 2 finden Sie in gitprotocol-v2[5].

Git Transport

Der Git-Transport beginnt damit, den Befehl und das Repository über das Netzwerk im pkt-line-Format zu senden, gefolgt von einem NUL-Byte und einem Hostnamen-Parameter, abgeschlossen durch ein NUL-Byte.

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

Der Transport kann zusätzliche Parameter senden, indem er ein zusätzliches NUL-Byte hinzufügt und dann eine oder mehrere NUL-terminierte Zeichenketten hinzufügt.

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

Der Host-Parameter wird für das namensbasierte virtuelle Hosting des git-daemon verwendet. Siehe die Option --interpolated-path für git daemon, mit den Formatzeichen %H/%CH.

Im Wesentlichen macht der Git-Client, um sich mit einem upload-pack-Prozess auf der Serverseite über das Git-Protokoll zu verbinden, Folgendes:

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

SSH Transport

Die Initiierung der Prozesse upload-pack oder receive-pack über SSH bedeutet die Ausführung des Binärprogramms auf dem Server über die SSH-Fernausführung. Es entspricht im Grunde der Ausführung von:

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

Damit ein Server Git-Pushes und -Pulls für einen bestimmten Benutzer über SSH unterstützt, muss dieser Benutzer in der Lage sein, einen oder beide dieser Befehle über die SSH-Shell auszuführen, die ihm bei der Anmeldung zur Verfügung gestellt wird. Auf einigen Systemen ist der Shell-Zugriff auf die Ausführung nur dieser beiden Befehle oder sogar nur eines davon beschränkt.

In einer ssh:// URI ist sie absolut in der URI, daher wird der / nach dem Hostnamen (oder der Portnummer) als Argument gesendet, das dann vom Remote-git-upload-pack genau so gelesen wird, was effektiv ein absoluter Pfad im entfernten Dateisystem ist.

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

In einer URI im Format "user@host:path" ist sie relativ zum Home-Verzeichnis des Benutzers, da der Git-Client ausführt:

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

Die Ausnahme ist, wenn ein ~ verwendet wird, in diesem Fall wird es ohne das führende / ausgeführt.

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

Abhängig vom Wert der Konfigurationsvariable protocol.version kann Git versuchen, zusätzliche Parameter als durch Doppelpunkte getrennte Zeichenkette in der Umgebungsvariable GIT_PROTOCOL zu senden. Dies geschieht nur, wenn die Konfigurationsvariable ssh.variant angibt, dass der ssh-Befehl das Übergeben von Umgebungsvariablen als Argument unterstützt.

Einige Dinge, die man sich hier merken sollte:

  • Der "Befehlsname" ist mit einem Bindestrich geschrieben (z. B. git-upload-pack), aber dies kann vom Client überschrieben werden.

  • Der Repository-Pfad ist immer in einfache Anführungszeichen gesetzt.

Daten von einem Server abrufen

Wenn ein Git-Repository Daten abrufen möchte, die ein zweites Repository besitzt, kann das erste vom zweiten fetchen. Diese Operation ermittelt, welche Daten der Server hat, die der Client nicht hat, und streamt diese Daten dann im Packfile-Format zum Client.

Referenzentdeckung

Wenn der Client die Verbindung initialisiert, antwortet der Server sofort mit einer Versionsnummer (wenn "version=1" als zusätzlicher Parameter gesendet wird) und einer Liste jeder Referenz, die er hat (alle Branches und Tags) zusammen mit dem Objekt-Namen, auf den jede Referenz derzeit zeigt.

 $ 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

Die zurückgegebene Antwort ist ein pkt-line-Stream, der jede Referenz und ihren aktuellen Wert beschreibt. Der Stream MUSS nach Namen gemäß der C-Locale-Sortierung sortiert sein.

Wenn HEAD eine gültige Referenz ist, MUSS HEAD als erste angebotene Referenz erscheinen. Wenn HEAD keine gültige Referenz ist, darf HEAD nicht in der Angebotsliste erscheinen, aber andere Referenzen können weiterhin erscheinen.

Der Stream MUSS Fähigkeitsdeklarationen hinter einem NUL beim ersten Ref enthalten. Der "gepeelte" Wert eines Refs (d.h. "ref^{}") MUSS sich unmittelbar nach dem Ref selbst befinden, wenn er präsentiert wird. Ein konformer Server MUSS das Ref "peelen", wenn es sich um einen annotierten Tag handelt.

  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

Server und Client MÜSSEN Kleinbuchstaben für obj-id verwenden, beide MÜSSEN obj-id als Groß-/Kleinschreibung-unempfindlich behandeln.

Siehe protocol-capabilities.txt für eine Liste zulässiger Serverfähigkeiten und Beschreibungen.

Packfile-Aushandlung

Nach der Referenz- und Fähigkeitsentdeckung kann der Client entscheiden, die Verbindung durch Senden eines flush-pkt zu beenden, was dem Server mitteilt, dass er sich nun ordnungsgemäß beenden und trennen kann, wenn er keine Packdaten benötigt. Dies kann bei einem ls-remote-Befehl geschehen und auch dann, wenn der Client bereits auf dem neuesten Stand ist.

Andernfalls tritt er in die Aushandlungsphase ein, in der der Client und der Server das minimale Packfile bestimmen, das für den Transport notwendig ist, indem er dem Server mitteilt, welche Objekte er benötigt, seine flachen Objekte (falls vorhanden) und die maximale Commit-Tiefe, die er wünscht (falls vorhanden). Der Client sendet auch eine Liste der Fähigkeiten, die er in Kraft haben möchte, aus denen, was der Server gesagt hat, dass er sie mit der ersten want-Zeile tun kann.

  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)

Clients MÜSSEN alle obj-ids senden, die sie aus der Referenzentdeckungsphase wünschen, als want-Zeilen. Clients MÜSSEN mindestens einen want-Befehl im Anfragetext senden. Clients DÜRFEN keine obj-id in einem want-Befehl erwähnen, die nicht in der Antwort der Ref-Entdeckung erschienen ist.

Der Client MUSS alle obj-ids schreiben, von denen er nur flache Kopien hat (was bedeutet, dass er die Eltern eines Commits nicht hat) als shallow-Zeilen, damit der Server über die Einschränkungen der Client-Historie informiert ist.

Der Client sendet nun die maximale Commit-Historientiefe, die er für diese Transaktion wünscht, d.h. die Anzahl der Commits, die er vom Ende der Historie wünscht, falls vorhanden, als deepen-Zeile. Eine Tiefe von 0 ist dasselbe wie keine Tiefenanfrage. Der Client möchte keine Commits über diese Tiefe hinaus empfangen, noch möchte er Objekte, die nur zum Vervollständigen dieser Commits benötigt werden. Commits, deren Eltern als Ergebnis nicht empfangen werden, werden als flach definiert und entsprechend auf dem Server markiert. Diese Informationen werden im nächsten Schritt an den Client zurückgesendet.

Der Client kann optional anfordern, dass pack-objects verschiedene Objekte aus dem Packfile weglässt, indem er eine von mehreren Filtertechniken verwendet. Diese sind für die Verwendung mit teilweisen Klon- und teilweisen Fetch-Vorgängen vorgesehen. Ein Objekt, das einem Filter-Spec-Wert nicht entspricht, wird weggelassen, es sei denn, es wird explizit in einer want-Zeile angefordert. Siehe rev-list für mögliche Filter-Spec-Werte.

Sobald alle want- und shallow-Zeilen (und optional deepen) übertragen wurden, MÜSSEN Clients einen flush-pkt senden, um der Serverseite mitzuteilen, dass die Liste fertig gesendet wurde.

Andernfalls, wenn der Client eine positive Tiefenanfrage gesendet hat, wird der Server bestimmen, welche Commits flach sind und welche nicht, und diese Informationen an den Client senden. Wenn der Client keine positive Tiefe angefordert hat, wird dieser Schritt übersprungen.

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

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

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

Wenn der Client eine positive Tiefe angefordert hat, berechnet der Server die Menge der Commits, die nicht tiefer als die gewünschte Tiefe sind. Die Menge der Commits beginnt bei den Wünschen des Clients.

Der Server schreibt shallow-Zeilen für jeden Commit, dessen Eltern als Ergebnis nicht gesendet werden. Der Server schreibt eine unshallow-Zeile für jeden Commit, den der Client als flach markiert hat, der aber bei der aktuell angeforderten Tiefe nicht mehr flach ist (d. h. seine Eltern werden nun gesendet). Der Server darf nichts als unshallow markieren, was der Client nicht als flach angegeben hat.

Nun sendet der Client eine Liste der obj-ids, die er hat, mit have-Zeilen, damit der Server ein Packfile erstellen kann, das nur die Objekte enthält, die der Client benötigt. Im Multi_ack-Modus sendet die kanonische Implementierung bis zu 32 davon auf einmal, dann sendet sie einen flush-pkt. Die kanonische Implementierung überspringt den nächsten Block und sendet sofort die nächsten 32, so dass immer ein Block von 32 "in-flight on the wire" vorhanden ist.

  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")

Wenn der Server have-Zeilen liest, antwortet er, indem er alle obj-ids, die der Client hatte, mit denen der Server ebenfalls übereinstimmt, bestätigt. Der Server bestätigt obj-ids unterschiedlich, je nachdem, welcher Bestätigungsmodus vom Client gewählt wurde.

Im Multi_ack-Modus

  • antwortet der Server mit ACK obj-id continue für alle gemeinsamen Commits.

  • Sobald der Server eine akzeptable gemeinsame Basis-Commit gefunden hat und bereit ist, ein Packfile zu erstellen, bestätigt er blind alle have obj-ids an den Client.

  • Der Server sendet dann einen NAK und wartet auf eine weitere Antwort vom Client – entweder eine done oder eine weitere Liste von have-Zeilen.

Im Multi_ack_detailed-Modus

  • unterscheidet der Server die Bestätigungen, indem er signalisiert, dass er bereit ist, Daten zu senden, mit ACK obj-id ready-Zeilen und signalisiert die identifizierten gemeinsamen Commits mit ACK obj-id common-Zeilen.

Ohne multi_ack oder multi_ack_detailed

  • sendet upload-pack "ACK obj-id" beim ersten gemeinsamen Objekt, das es findet. Danach sagt es nichts mehr, bis der Client "done" sendet.

  • upload-pack sendet "NAK" auf einem flush-pkt, wenn noch kein gemeinsames Objekt gefunden wurde. Wenn eines gefunden wurde und somit bereits eine Bestätigung gesendet wurde, ist es beim flush-pkt still.

Nachdem der Client genügend ACK-Antworten erhalten hat, um festzustellen, dass der Server genügend Informationen hat, um ein effizientes Packfile zu senden (in der kanonischen Implementierung wird dies festgestellt, wenn er genügend ACKs erhalten hat, um alles im --date-order-Queue als gemeinsam mit dem Server zu färben, oder die --date-order-Queue leer ist), oder der Client feststellt, dass er aufgeben möchte (in der kanonischen Implementierung wird dies festgestellt, wenn der Client 256 have-Zeilen sendet, ohne dass eine davon vom Server bestätigt wird – was bedeutet, dass nichts gemeinsam ist und der Server alle seine Objekte senden sollte), dann sendet der Client einen done-Befehl. Der done-Befehl signalisiert dem Server, dass der Client bereit ist, seine Packfile-Daten zu empfangen.

Das Limit von 256 wird in der kanonischen Client-Implementierung jedoch nur aktiviert, wenn wir während einer früheren Runde mindestens ein "ACK %s continue" erhalten haben. Dies hilft sicherzustellen, dass mindestens ein gemeinsamer Vorfahre gefunden wird, bevor wir komplett aufgeben.

Sobald die done-Zeile vom Client gelesen wurde, sendet der Server entweder ein abschließendes ACK obj-id oder ein NAK. obj-id ist der Objekt-Name des letzten als gemeinsam bestimmten Commits. Der Server sendet ACK nach done nur, wenn mindestens eine gemeinsame Basis vorhanden ist und multi_ack oder multi_ack_detailed aktiviert ist. Der Server sendet immer NAK nach done, wenn keine gemeinsame Basis gefunden wurde.

Anstelle von ACK oder NAK kann der Server eine Fehlermeldung senden (z. B. wenn er ein Objekt in einer want-Zeile, die vom Client empfangen wurde, nicht erkennt).

Dann beginnt der Server mit dem Senden seiner Packfile-Daten.

  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")

Ein einfacher Klon könnte so aussehen (ohne have-Zeilen)

   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]

Eine inkrementelle Update-Antwort (Fetch) könnte so aussehen

   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]

Packfile-Daten

Nachdem der Client und der Server die Verhandlung über die minimale Datenmenge, die an den Client gesendet werden muss, abgeschlossen haben, konstruiert und sendet der Server die erforderlichen Daten im Packfile-Format.

Siehe gitformat-pack[5], wie das Packfile selbst tatsächlich aussieht.

Wenn side-band- oder side-band-64k-Fähigkeiten vom Client angegeben wurden, sendet der Server die Packfile-Daten multiplexed.

Jedes Paket beginnt mit der Paketlinienlänge der folgenden Daten, gefolgt von einem einzelnen Byte, das die Sideband angibt, auf der die folgenden Daten eingehen.

Im side-band-Modus werden bis zu 999 Datenbytes plus 1 Kontrollcode gesendet, insgesamt bis zu 1000 Bytes in einer pkt-line. Im side-band-64k-Modus werden bis zu 65519 Datenbytes plus 1 Kontrollcode gesendet, insgesamt bis zu 65520 Bytes in einer pkt-line.

Das Sideband-Byte ist eine 1, 2 oder 3. Sideband 1 enthält Packfile-Daten, Sideband 2 wird für Fortschrittsinformationen verwendet, die der Client normalerweise an stderr ausgibt, und Sideband 3 wird für Fehlerinformationen verwendet.

Wenn keine side-band-Fähigkeit angegeben wurde, streamt der Server das gesamte Packfile ohne Multiplexing.

Daten an einen Server pushen

Das Pushen von Daten an einen Server ruft den Prozess receive-pack auf dem Server auf, der es dem Client ermöglicht, ihm mitzuteilen, welche Referenzen er aktualisieren soll, und dann alle Daten zu senden, die der Server für die Vervollständigung dieser neuen Referenzen benötigt. Sobald alle Daten empfangen und validiert wurden, aktualisiert der Server seine Referenzen auf das vom Client angegebene.

Authentifizierung

Das Protokoll selbst enthält keine Authentifizierungsmechanismen. Dies muss vom Transport, wie z. B. SSH, gehandhabt werden, bevor der Prozess receive-pack aufgerufen wird. Wenn receive-pack über den Git-Transport konfiguriert ist, sind diese Repositories für jeden schreibbar, der auf diesen Port (9418) zugreifen kann, da dieser Transport unauthentifiziert ist.

Referenzentdeckung

Die Referenzentdeckungsphase erfolgt fast auf die gleiche Weise wie im Fetch-Protokoll. Jede Referenz obj-id und Name auf dem Server wird im Packet-Line-Format an den Client gesendet, gefolgt von einem flush-pkt. Der einzige wirkliche Unterschied ist, dass die Fähigkeitsliste anders ist – die einzigen möglichen Werte sind report-status, report-status-v2, delete-refs, ofs-delta, atomic und push-options.

Referenzaktualisierungsanfrage und Packfile-Übertragung

Sobald der Client weiß, auf welchem Stand sich die Referenzen des Servers befinden, kann er eine Liste von Referenzaktualisierungsanfragen senden. Für jede Referenz auf dem Server, die er aktualisieren möchte, sendet er eine Zeile mit der obj-id, die sich derzeit auf dem Server befindet, der obj-id, auf die der Client sie aktualisieren möchte, und dem Namen der Referenz.

Dieser Liste folgt ein 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 )

Wenn der Server die push-options-Fähigkeit beworben hat und der Client push-options als Teil der obigen Fähigkeitsliste angegeben hat, sendet der Client anschließend seine Push-Optionen, gefolgt von einem flush-pkt.

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

Zur Abwärtskompatibilität mit älteren Git-Servern, wenn der Client ein Push-Zertifikat und Push-Optionen sendet, MUSS er seine Push-Optionen sowohl eingebettet im Push-Zertifikat als auch nach dem Push-Zertifikat senden. (Beachten Sie, dass die Push-Optionen innerhalb des Zertifikats ein Präfix haben, die Push-Optionen nach dem Zertifikat jedoch nicht.) Beide Listen MÜSSEN identisch sein, modulo des Präfixes.

Danach wird das Packfile gesendet, das alle Objekte enthalten soll, die der Server zum Vervollständigen der neuen Referenzen benötigt.

  packfile          =  "PACK" 28*(OCTET)

Wenn das empfangende Ende delete-refs nicht unterstützt, darf das sendende Ende keinen delete-Befehl anfordern.

Wenn das empfangende Ende push-cert nicht unterstützt, darf das sendende Ende keinen push-cert-Befehl senden. Wenn ein push-cert-Befehl gesendet wird, darf die command-list NICHT gesendet werden; stattdessen werden die im Push-Zertifikat aufgezeichneten Befehle verwendet.

Das Packfile darf NICHT gesendet werden, wenn der einzige verwendete Befehl delete ist.

Ein Packfile MUSS gesendet werden, wenn ein create- oder update-Befehl verwendet wird, auch wenn der Server bereits alle notwendigen Objekte hat. In diesem Fall MUSS der Client ein leeres Packfile senden. Der einzige Fall, in dem dies wahrscheinlich vorkommt, ist, wenn der Client einen neuen Branch oder einen Tag erstellt, der auf eine vorhandene obj-id verweist.

Der Server empfängt das Packfile, entpackt es, validiert dann jede zu aktualisierende Referenz, die sich nicht geändert hat, während die Anfrage bearbeitet wurde (die obj-id ist immer noch dieselbe wie die old-id), und führt alle Update-Hooks aus, um sicherzustellen, dass die Aktualisierung akzeptabel ist. Wenn alles in Ordnung ist, aktualisiert der Server die Referenzen.

Push-Zertifikat

Ein Push-Zertifikat beginnt mit einer Reihe von Header-Zeilen. Nach dem Header und einer leeren Zeile folgen die Protokollbefehle, einer pro Zeile. Beachten Sie, dass das nachgestellte LF in Push-Cert PKT-LINEs NICHT optional ist; es muss vorhanden sein.

Derzeit sind die folgenden Header-Felder definiert:

pusher Ident

Identifiziert den GPG-Schlüssel im Format "Human Readable Name <email@address>".

pushee URL

Die Repository-URL (anonymisiert, falls die URL Authentifizierungsmaterialien enthält), in die der Benutzer, der git push ausgeführt hat, pushen wollte.

nonce nonce

Die nonce-Zeichenkette, die das empfangende Repository den pushenden Benutzer gebeten hat, in das Zertifikat aufzunehmen, um Replay-Angriffe zu verhindern.

Die GPG-Signaturzeilen sind eine getrennte Signatur für den Inhalt, der im Push-Zertifikat vor Beginn des Signaturblocks aufgezeichnet wurde. Die getrennte Signatur wird verwendet, um zu zertifizieren, dass die Befehle vom Pusher gegeben wurden, der der Unterzeichner sein muss.

Statusberichterstattung

Nachdem die Packdaten vom Sender empfangen wurden, sendet der Empfänger einen Bericht, wenn die Fähigkeit report-status oder report-status-v2 aktiv ist. Es ist eine kurze Auflistung dessen, was bei dieser Aktualisierung passiert ist. Zuerst wird der Status des Packfile-Entpackens als entweder unpack ok oder unpack [error] aufgeführt. Dann wird der Status für jede der zu aktualisierenden Referenzen aufgelistet. Jede Zeile ist entweder ok [refname], wenn die Aktualisierung erfolgreich war, oder ng [refname] [error], wenn sie fehlgeschlagen ist.

  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"

Die Fähigkeit report-status-v2 erweitert das Protokoll durch Hinzufügen neuer Option-Zeilen, um die Berichterstattung über Referenzen zu unterstützen, die vom proc-receive-Hook umgeschrieben wurden. Der proc-receive-Hook kann einen Befehl für eine Pseudoreferenz verarbeiten, die eine oder mehrere Referenzen erstellen oder aktualisieren kann, und jede Referenz kann einen anderen Namen, eine andere new-oid und eine andere old-oid haben.

  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")

Aktualisierungen können aus verschiedenen Gründen fehlschlagen. Die Referenz kann sich seit der ursprünglichen Sendung der Referenzentdeckungsphase geändert haben, was bedeutet, dass jemand dazwischen gepusht hat. Die gepushte Referenz könnte eine nicht-fast-forward-Referenz sein und die Update-Hooks oder die Konfiguration könnten so eingestellt sein, dass dies nicht erlaubt ist, usw. Außerdem können einige Referenzen aktualisiert werden, während andere abgelehnt werden können.

Eine Beispielkommunikation zwischen Client und Server könnte so aussehen:

   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

Teil der git[1] Suite