2015-01-25 21 views
5

Ich habe here geschaut aber konnte nicht ganz die Dinge herausfinden, über die ich mich wunderte: Wie findet git push oder git pull herauszufinden, was Commit-Objekte auf der anderen Seite fehlen?Wie bestimmt Git, welche Objekte zwischen den Repositories gesendet werden müssen?

Lassen Sie uns sagen, dass wir ein Repository mit den folgenden Commits haben: (Buchstaben stehen in für SHA-1-IDs, d ist refs/heads/master)

a -> b -> c -> d 

Die Fernbedienung im Gegensatz dazu hat diese:

a -> e -> f -> g 

Laut dem Git-Dokument, die Fernbedienung würde uns sagen, dass refs/heads/master ist g, aber da wir nicht wissen, dass commit, das sagt uns eigentlich nichts. Wie ist das genug, um die fehlenden Daten herauszufinden?


In der anderen Richtung, heißt es im Dokument:

An diesem Punkt der Fetch-Pack Prozess betrachtet, welche Objekte es hat und reagiert mit den Objekten, die sie durch das Senden muss „wollen“ und dann die SHA-1, die es will. Es sendet alle Objekte, die es bereits hat mit "haben" und dann die SHA-1. Am Ende dieser Liste, schreibt sie „fertig“ den Upload-Pack-Prozess zu initiieren, um die Packfile der Daten zu beginnen Senden sie brauchen:

dies erklärt, wie die Fernbedienung würde bestimmen, welche Daten zu senden, aber wouldn Trifft diese Auswirkung die Leistung auf Repositories mit vielen Objekten? Was ist sonst eigentlich im Text gemeint?


Anscheinend ist der Weg der Datenübertragung sehr unterschiedlich in Abhängigkeit von der Richtung (Push vs Pull). Was und wie sind die Herausforderungen, die mit dieser Designauswahl verbunden sind, und wie soll ich ihre Beschreibungen im Dokument verstehen?

Antwort

10

Die Magie ist in den IDs. Eine Commit ID besteht aus vielen Dingen, aber im Grunde ist es eine SHA-1 hash davon.

  • Inhalt (alles, nicht nur die diff)
  • Autor
  • Datum
  • Log Nachricht
  • Eltern IDs

Ändern Sie diese und Sie brauchen einen neuen erstellen Commit mit einer neuen ID. Beachten Sie, dass die übergeordneten IDs enthalten sind.

Was bedeutet das für Git? Das heißt, wenn ich Ihnen sage, dass ich "ABC123" habe und Sie "ABC123" committen, wissen wir, dass wir denselben Commit mit demselben Inhalt, demselben Autor, demselben Datum, derselben Nachricht und denselben Elternteilen haben. Diese Eltern haben die gleiche ID, also haben sie denselben Inhalt, denselben Autor, dasselbe Datum, dieselbe Nachricht, und dieselben Eltern. Und so weiter. Wenn die IDs übereinstimmen, sie müssen die gleiche Geschichte haben, es gibt keine Notwendigkeit, weiter unten in der Zeile zu überprüfen.Dies ist eine der großen Stärken von Git, es ist tief in sein Design eingewoben und du kannst Git nicht ohne es verstehen.

Ein Pull ist ein Fetch plus eine Zusammenführung. git pull origin master ist git fetch origin plus git merge master origin/master (oder rebase mit --rebase). A wie folgt aussieht etwas holen ...

remote @ http://example.com/project.git 

        F - G [bugfix] 
       /
A - B - C - D - E - J [master] 
        \ 
         H - I [feature] 

local 
origin = http://example.com/project.git 

        F - G [origin/bugfix] 
       /
A - B - C - D - E [origin/master] [master] 
  • [local] Hey Fern, welche Zweige haben Sie?
  • [remote] Ich habe Bugfix bei G.
  • [local] Ich habe auch Bugfix bei G! Erledigt. Was sonst?
  • [remote] Ich habe Feature bei I.
  • [local] Ich habe keine Funktion noch ich. Was sind die Eltern von I?
  • [remote] Ich bin der Elternteil H.
  • [local] Ich habe nicht H, was ist Hs Eltern?
  • [remote] H's Eltern sind J.
  • [local] Ich habe keine J. Was ist J's Eltern?
  • [remote] J Elternteil ist E.
  • [local] Ich habe E! Schick mir bitte J, H und ich.
  • [remote] Ok, hier kommen sie.
  • [lokal] fügt J, H und I zum Repo hinzu und setzt Ursprung/Merkmal auf I Ok, was hast du noch?
  • [remote] Ich habe Meister J.
  • [local] I Master an E haben, schickte man mich schon J. bewegt Herkunft/Master J. Was sonst?
  • [remote] Das ist es!
  • [local] Kthxbi

Und jetzt lokale sieht wie folgt aus ...

local 
origin = http://example.com/project.git 

        F - G [origin/bugfix] 
       /
A - B - C - D - E [master] - J [origin/master] 
           \ 
           H - I [origin/feature] 

Dann wird es git merge master origin/master tun den Zug zu beenden, das wird schnell vorwärts zu J.

Ein Push ist ähnlich, nur dass der Prozess umgekehrt wird (lokal sendet Commits an die Remote) und es wird nur im Schnellvorlauf weitergeschaltet.

Dies ist Pro Git refers to as "the dumb protocol" und wird verwendet, wenn Ihre Fernbedienung ein einfacher HTTP-Server ist. wird häufiger verwendet, ist viel weniger gesprächig und hat viele Optimierungen. Aber Sie können sehen, wie entweder schrecklich effizient sein kann. Es ist nicht notwendig, den gesamten Verlauf zu kommunizieren, sie müssen nur 20-Byte-Hash-Schlüssel senden, bis sie einen gemeinsamen Vorfahren finden.

Hier sind einige Quellen und weitere Lektüre.

+0

Ihre Antwort ist sehr gut, danke! Wenn es Ihnen nichts ausmacht, interessiert mich besonders, wie Sie die vielen Rundreisen vermeiden können. Ist es einfach eine Angelegenheit, eifrig z.B.bis zu 50 Hashes (<1kb), oder gibt es einen eher theoretischen, algorithmischen Mechanismus, der schneller feststellen kann, ob ein Commit bekannt ist? –

+1

Auch, wenn Sie irgendwelche Quellen für Ihre Antwort haben, wären diese großartig. Mein Anliegen war mehr "die Implementierung eines Git-ähnlichen Synchronisationsmechanismus", also bin ich voll zufrieden, aber ein zukünftiger Leser, der nach "wie GIT-Synchronisierung ist im Detail implementiert" sucht, könnte es zu schätzen wissen. –

+0

@SillyFreak Ich fügte ein paar Referenzen zu weiteren Details hinzu. Das Konversationsbeispiel ist, wie ich es konzeptionell lehre, was Pro Git ["The Dumb Protocol"] nennt (http://git-scm.com/book/en/v2/Git-Internals-Transfer-Protocols#The-Dumb- Protokoll). Sie möchten ["The Smart Protocol"] (http://git-scm.com/book/de/v2/Git-Internals-Transfer-Protocols#The-Smart-Protocol) für ein effizienteres Beispiel betrachten. – Schwern