Heim  >  Artikel  >  Computer-Tutorials  >  Sind Git-Commits Unterschiede, Snapshots oder Verlauf?

Sind Git-Commits Unterschiede, Snapshots oder Verlauf?

PHPz
PHPznach vorne
2024-02-19 11:39:421106Durchsuche

Git 提交是差异、快照还是历史记录?

Für mich ist es leicht zu verstehen, wie Git-Commits implementiert werden, aber es ist schwierig zu verstehen, wie andere Commits sehen. Also habe ich anderen auf Mastodon einige Fragen gestellt.

Was halten Sie von der Git-Einreichung?

Ich habe eine sehr unwissenschaftliche Umfrage durchgeführt und die Leute gefragt, was sie von Git-Commits halten: Ist es ein Snapshot, ein Diff oder eine Liste aller vorherigen Commits? (Natürlich ist es vernünftig, sich alle drei vorzustellen, aber ich bin neugierig auf die wichtigsten

der Leute

Das Ergebnis ist:

  • 51 % Unterschied
  • 42 % Schnappschuss
  • 4 % Verlauf aller vorherigen Commits
  • 3 % „Andere“

Ich bin überrascht, wie nahe die Verhältnisse der beiden Optionen in Differenz und Schnappschuss liegen. Die Leute haben auch einige interessante, aber widersprüchliche Punkte angesprochen, wie
„Mir scheint, dass ein Commit ein Diff ist, aber ich denke, dass er tatsächlich als Snapshot implementiert ist“ und
„Mir scheint, dass ein Commit ein Snapshot ist, aber ich denke es kommt tatsächlich in Form eines Unterschieds vor.“ Wir werden später mehr darüber sprechen, wie die Einreichung tatsächlich umgesetzt wird.

Bevor wir weitermachen: Was meinen wir mit „einem Unterschied“ oder „einer Momentaufnahme“?

Was ist der Unterschied?

Der „Unterschied“, von dem ich spreche, ist wahrscheinlich ziemlich offensichtlich: Der Unterschied besteht darin, was man bekommt, wenn man läuft git show COMMIT_ID . Hier ist zum Beispiel eine Tippfehlerkorrektur im rbspy-Projekt:

diff --git a/src/ui/summary.rs b/src/ui/summary.rs
index 5c4ff9c..3ce9b3b 100644
--- a/src/ui/summary.rs
+++ b/src/ui/summary.rs
@@ -160,7 +160,7 @@ mod tests {
";
let mut buf: Vec = Vec::new();
-stats.write(&mut buf).expect("Callgrind write failed");
+stats.write(&mut buf).expect("summary write failed");
let actual = String::from_utf8(buf).expect("summary output not utf8");
assert_eq!(actual, expected, "Unexpected summary output");
}

Sie können es auf GitHub sehen: https://github.com/rbspy/rbspy/commit/24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b

Was ist ein Schnappschuss?

Mit „Schnappschuss“ meine ich „alle Dateien, die Sie erhalten, wenn Sie git checkout COMMIT_ID ausführen“.

Git bezeichnet die Liste der übermittelten Dateien normalerweise als „Baum“ (z. B. einen „Verzeichnisbaum“), und Sie können alle oben übermittelten Dateien auf GitHub sehen:

https://github.com/rbspy/rbspy/tree/24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b (es ist /tree/ 而不是 /commit/)

Ist „wie Git implementiert wird“ wirklich die richtige Art, es zu erklären?

Der Rat, den ich zum Erlernen von Git am häufigsten höre, ist wahrscheinlich: „Lernen Sie einfach, wie Git die Dinge intern darstellt, und alles wird klarer.“ Ich liebe diese Perspektive offensichtlich sehr (wenn Sie einige Zeit damit verbracht haben, diesen Blog zu lesen, wissen Sie, dass ich sie liebe

Aber als Möglichkeit, Git zu lernen, hat es nicht so gut geklappt, wie ich gehofft hatte! Normalerweise würde ich aufgeregt anfangen zu erklären: „Okay, also Git
ein Commit ist ein Snapshot, es hat einen Zeiger auf seinen übergeordneten Commit, dann ist ein Zweig ein Zeiger auf den Commit, dann ...“, aber ich versuche zu helfen Die Leute werden mir sagen, dass sie diese Erklärung nicht wirklich hilfreich fanden und sie immer noch nicht verstehen. Deshalb habe ich mir andere Optionen angesehen.

Aber lassen Sie uns zunächst über die interne Umsetzung sprechen.

Wie Git Commits intern darstellt – Snapshot

Intern stellt Git Commits als Snapshots dar (es speichert einen „Baum“ der aktuellen Version jeder Datei). Ich befinde mich in einem Git-Repository. Wo sind Ihre Dateien? Ich habe darüber in geschrieben, aber hier ist ein sehr kurzer Überblick über das interne Format.

Dies ist eine Einreichungsdarstellung:

$ git cat-file -p 24ad81d2439f9e63dd91cc1126ca1bb5d3a4da5b
tree e197a79bef523842c91ee06fa19a51446975ec35
parent 26707359cdf0c2db66eb1216bf7ff00eac782f65
author Adam Jensen1672104452 -0500
committer Adam Jensen1672104890 -0500
Fix typo in expectation message

Und wenn wir uns dieses Baumobjekt ansehen, sehen wir eine Liste aller Dateien/Unterverzeichnisse im Stammverzeichnis des Repositorys in diesem Commit:

$ git cat-file -p e197a79bef523842c91ee06fa19a51446975ec35
040000 tree 2fcc102acd27df8f24ddc3867b6756ac554b33ef.cargo
040000 tree 7714769e97c483edb052ea14e7500735c04713eb.github
100644 blob ebb410eb8266a8d6fbde8a9ffaf5db54a5fc979a.gitignore
100644 blob fa1edfb73ce93054fe32d4eb35a5c4bee68c5bf5ARCHITECTURE.md
100644 blob 9c1883ee31f4fa8b6546a7226754cfc84ada5726CODE_OF_CONDUCT.md
100644 blob 9fac1017cb65883554f821914fac3fb713008a34CONTRIBUTORS.md
100644 blob b009175dbcbc186fb8066344c0e899c3104f43e5Cargo.lock
100644 blob 94b87cd2940697288e4f18530c5933f3110b405bCargo.toml

Das bedeutet, dass das Auschecken eines Git-Commits immer schnell geht: Es ist für Git genauso einfach, den Commit von gestern auszuchecken, wie das Auschecken von Commits von vor einer Million. Git muss nie 10.000 Diffs erneut anwenden, um den aktuellen Status zu ermitteln, da Commits nie als Diffs gespeichert werden.

Schnappschüsse werden mit Packfile komprimiert

Ich habe gerade erwähnt, dass ein Git-Commit ein Snapshot ist, aber wenn jemand sagt „Meiner Meinung nach ist ein Commit ein Snapshot, aber ich denke, es ist ein Unterschied in der Implementierung“
, dann stimmt das tatsächlich auch! Git-Commits
werden nicht in der Form von Diffs dargestellt, an die Sie vielleicht gewöhnt sind (sie werden nicht als Diff zum vorherigen Commit auf der Festplatte gespeichert), aber die grundlegende Intuition ist, dass Sie 10.000
ausführen möchten line file 500 Mal zu bearbeiten und dann 500 Dateien zu speichern, ist ineffizient.

Git bietet eine Möglichkeit, Dateien als Diffs zu speichern. Dies wird als „Packdatei“ bezeichnet und Git sammelt Ihre Daten regelmäßig in einer Packdatei, um Speicherplatz zu sparen. Git komprimiert auch Daten, wenn Sie ein Repository erstellen. git clone

Ich habe hier nicht genug Platz, um vollständig zu erklären, wie Packdateien funktionieren (Aditya Mukerjees „Unpacking Git Packfiles“ ist mein Lieblingsartikel, in dem erklärt wird, wie sie funktionieren). Allerdings kann ich hier kurz mein Verständnis darüber zusammenfassen, wie Deltas funktionieren und wie sie sich von Diff unterscheiden:

    Das Objekt wird als Referenz zur „Originaldatei“ und als „Delta“ gespeichert
  • Ein Delta ist eine Folge von Anweisungen wie zum Beispiel „Lesen Sie die Bytes 0 bis 100, fügen Sie dann das Byte ‚Hallo da‘ ein und lesen Sie dann die Bytes 120 bis 200.“ Es setzt neuen Text aus den Originaldateien zusammen. Es gibt also kein Konzept des „Löschens“, sondern nur Kopieren und Hinzufügen.
  • Ich glaube, es gibt weniger Delta-Ebenen: Ich weiß nicht, wie ich genau überprüfen kann, wie viele Delta-Ebenen Git durchlaufen muss, um ein bestimmtes Objekt zu erhalten, aber ich habe den Eindruck, dass es normalerweise nicht viele sind. Vielleicht weniger als 10 Stockwerke? Ich würde allerdings gerne wissen, wie ich das eigentlich herausfinden kann.
  • Die Originaldatei muss nicht aus dem vorherigen Commit stammen, sie kann alles sein. Vielleicht könnte es sogar von einem späteren Commit stammen? Ich bin nicht sicher.
  • Es gibt keinen „richtigen“ Algorithmus zur Berechnung von Änderungen, Git verfügt nur über einige ungefähre Heuristiken

Etwas Seltsames passiert tatsächlich, wenn man sich die Unterschiede ansieht

Was tatsächlich passiert, wenn wir

ausführen, um den Unterschied eines Commits zu sehen, ist ein wenig kontraintuitiv. Mein Verständnis ist: git show SOME_COMMIT

Git sucht in der Packdatei und wendet die Änderungen an, um den Baum dieses Commits und seiner übergeordneten Commits neu zu erstellen.
  • Git führt einen Differenzvergleich zwischen zwei Verzeichnisbäumen durch (dem Verzeichnisbaum des aktuellen Commits und dem Verzeichnisbaum des übergeordneten Commits). Das geht normalerweise schnell, weil fast alle Dateien genau gleich sind, sodass Git einfach die Hashes identischer Dateien vergleichen kann, ohne dass dabei fast immer etwas passiert.
  • Endlich zeigt Git die Unterschiede
  • Git wandelt die Änderungen also in einen Snapshot um und berechnet dann die Differenz. Es fühlt sich ein wenig seltsam an, weil es mit so etwas wie einem Unterschied beginnt und mit etwas anderem wie einem Unterschied endet, aber das Ausmaß der Veränderung und der Unterschied sind eigentlich völlig unterschiedlich, also macht es Sinn.

    Trotzdem denke ich, dass Git-Speicher-Commits als Snapshots und Packfile nur ein Implementierungsdetail sind, um Speicherplatz zu sparen und das Klonen zu beschleunigen. Ich musste eigentlich nie wissen, wie Packfile funktioniert, aber es hilft mir zu verstehen, wie Git Snapshots festschreibt, ohne zu viel Speicherplatz zu beanspruchen.

    Ein „falsches“ Git-Verständnis: Commits sind Unterschiede

    Ich denke, ein ziemlich verbreitetes Verständnis von Gits „Fehlern“ ist:

      Commits werden als Diffs basierend auf dem vorherigen Commit gespeichert (plus einem Zeiger auf den übergeordneten Commit, den Autor und die Nachricht).
    • Um den aktuellen Status eines Commits zu erhalten, muss Git alle vorherigen Commits von Grund auf erneut anwenden.
    Dieses Verständnis ist natürlich falsch (in Wirklichkeit werden Commits in Form von Snapshots gespeichert und Diffs aus diesen Snapshots berechnet), aber es scheint mir sehr nützlich und sinnvoll! Es ist ein wenig seltsam, wenn man über Merge-Commits nachdenkt, aber vielleicht könnten wir sagen, dass es nur der Unterschied ist, der auf dem ersten übergeordneten Commit des Merge-Commits basiert.

    Ich denke, dieses Missverständnis ist manchmal sehr nützlich und scheint für den täglichen Git-Gebrauch kein Problem zu sein. Mir gefällt wirklich, dass es die Dinge, die wir am häufigsten verwenden (Unterschiede), zu den grundlegendsten Elementen macht – es ist für mich sehr intuitiv.

    Ich habe auch über einige andere nützliche, aber „falsche“ Verständnisse von Git nachgedacht, wie zum Beispiel:

    • Commit-Informationen können bearbeitet werden (eigentlich nicht, Sie kopieren einfach einen identischen Commit und geben ihm neue Informationen, der alte Commit existiert noch)
    • Commits können in eine andere Basis verschoben werden (ebenso werden sie kopiert)

    Ich denke, es gibt eine Reihe „falscher“ Verständnisse von Git, die sehr sinnvoll sind, von der Git-Benutzeroberfläche weitgehend unterstützt werden und in den meisten Fällen keine Probleme verursachen. Es kann jedoch verwirrend werden, wenn Sie eine Änderung rückgängig machen möchten oder etwas schief geht.

    Einige Vorteile, wenn man Einreichungen als Differenzen betrachtet

    Obwohl ich weiß, dass Commits Snapshots in Git sind, behandle ich sie wahrscheinlich die meiste Zeit als Diffs, weil:

    • Meistens konzentriere ich mich auf die Änderungen, die ich vornehme – wenn ich nur eine Codezeile ändere, denke ich natürlich hauptsächlich an diese Codezeile und nicht an den aktuellen Status der gesamten Codebasis
    • Sie werden den Unterschied sehen, wenn Sie auf GitHub auf Git-Commit klicken oder git show verwenden, also ist es einfach etwas, was ich gewohnt bin zu sehen
    • Ich verwende Rebasing häufig, es geht darum, Unterschiede erneut anzuwenden

    Einige Vorteile der Behandlung von Commits als Snapshots

    Aber manchmal stelle ich mir Commits auch als Schnappschüsse vor, weil:

    • Git ist oft durch das Verschieben von Dateien verwirrt: Manchmal verschiebe ich eine Datei und bearbeite sie, und Git erkennt nicht, dass sie verschoben wurde, stattdessen wird sie als
      angezeigt „old.py entfernt, new.py hinzugefügt“. Das liegt daran, dass Git nur Snapshots speichert, also wenn dort „Verschiebe alte.py -> neue.py“ steht
      Derzeit ist dies nur eine Vermutung, da der Inhalt von old.py und new.py ähnlich ist.
    • Auf diese Weise ist es einfacher zu verstehen, was git checkout COMMIT_ID tut (der Gedanke, 10.000 Commits erneut anzuwenden, stresst mich)
    • Merge-Commits sehen für mich eher wie Schnappschüsse aus, da der zusammengeführte Commit buchstäblich alles sein kann (es ist nur ein neuer Snapshot!). Es hat mir geholfen zu verstehen, warum bei der Lösung von Zusammenführungskonflikten willkürliche Änderungen vorgenommen werden können und warum bei der Lösung von Konflikten Vorsicht geboten ist.

    Einige andere Verständnisse zur Unterwerfung

    Einige Antworten von Mastodon erwähnten auch:

    • „Zusätzliche“ Out-of-Band-Informationen zu Commits, wie E-Mails, GitHub-Pull-Requests oder Gespräche, die Sie mit Kollegen führen
    • Stellen Sie sich „Unterschied“ als einen „Zustand davor + Zustand danach“ vor
    • Und natürlich sehen viele Menschen Einreichungen je nach den Umständen unterschiedlich

    Einige andere Wörter, die Leute verwenden, wenn sie über Commits sprechen, die möglicherweise weniger mehrdeutig sind:

    • „Revision“ (scheint eher eine Momentaufnahme zu sein)
    • „Patch“ (sieht eher aus wie Diff)

    Das ist es!

    Es fällt mir schwer, die unterschiedlichen Vorstellungen der Menschen von Git zu verstehen. Besonders heikel ist, dass, obwohl „falsche“ Verständnisse oft sehr nützlich sind, die Menschen so sehr auf der Hut vor „falschen“ mentalen Modellen sind, dass sie ihre „falschen“ Ideen nur ungern mitteilen, aus Angst, dass irgendein Git-Interpreter aufsteht. Kommen Sie heraus und erklären Sie ihnen, warum sie falsch liegen. (Diese Git
    -Interpreter meinen es normalerweise gut, aber es kann trotzdem negative Auswirkungen haben)

    Aber ich habe viel gelernt! Ich bin mir immer noch nicht ganz sicher, wie ich über Commits sprechen soll, aber wir werden es irgendwann herausfinden.

    Vielen Dank an Marco Rogers, Marie Flanagan und alle bei Mastodon, die mit mir über Git-Commits gesprochen haben.

    Das obige ist der detaillierte Inhalt vonSind Git-Commits Unterschiede, Snapshots oder Verlauf?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:mryunwei.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen