Zitat von Linus Torvalds auf die Frage, wie viele Dateien Git während seines Tech Talk bei Google 2007 (43:09) verarbeiten kann:
… Git verfolgt Ihren Inhalt. Es wird niemals eine einzelne Datei verfolgt. Sie können eine Datei in Git nicht verfolgen. Was Sie tun können, ist, dass Sie ein Projekt verfolgen können, das eine einzelne Datei enthält. Wenn Ihr Projekt jedoch eine einzelne Datei enthält, tun Sie dies auf jeden Fall und Sie können es tun. Wenn Sie jedoch 10.000 Dateien verfolgen, sieht Git diese niemals als einzelne Dateien an. Git denkt alles als vollen Inhalt. Die gesamte Geschichte in Git basiert auf der Geschichte des gesamten Projekts…
(Transkripte hier .)
Doch wenn man in taucht das Git Buch , das erste , was Sie gesagt werden , ist , dass eine Datei in Git kann entweder verfolgt oder untracked . Außerdem scheint mir das gesamte Git-Erlebnis auf die Versionierung von Dateien ausgerichtet zu sein. Bei Verwendung git diff
oder git status
Ausgabe wird pro Datei dargestellt. Bei der Verwendung können git add
Sie auch pro Datei auswählen. Sie können den Verlauf sogar auf Dateibasis überprüfen und sind blitzschnell.
Wie ist diese Aussage zu interpretieren? Wie unterscheidet sich Git in Bezug auf die Dateiverfolgung von anderen Versionsverwaltungssystemen wie CVS?
quelle
Antworten:
In CVS wurde der Verlauf pro Datei verfolgt. Ein Zweig kann aus verschiedenen Dateien mit eigenen Revisionen bestehen, von denen jede ihre eigene Versionsnummer hat. CVS basierte auf RCS ( Revision Control System ), das einzelne Dateien auf ähnliche Weise verfolgte.
Auf der anderen Seite macht Git Schnappschüsse vom Status des gesamten Projekts. Dateien werden nicht unabhängig voneinander verfolgt und versioniert. Eine Revision im Repository bezieht sich auf einen Status des gesamten Projekts, nicht auf eine Datei.
Wenn Git sich auf das Verfolgen einer Datei bezieht, bedeutet dies einfach, dass sie in den Verlauf des Projekts aufgenommen werden soll. Linus 'Vortrag bezog sich nicht auf Tracking-Dateien im Git-Kontext, sondern stellte das CVS- und RCS-Modell dem in Git verwendeten Snapshot-basierten Modell gegenüber.
quelle
$Id$
in einer Datei verwenden können. Das gleiche funktioniert nicht in Git, weil das Design unterschiedlich ist.Ich stimme brian m zu. Carlsons Antwort : Linus unterscheidet tatsächlich zumindest teilweise zwischen dateiorientierten und festschreibungsorientierten Versionskontrollsystemen. Aber ich denke, da steckt noch mehr dahinter.
Im meinem Buch , das ins Stocken gerät und möglicherweise nie fertig wird, habe ich versucht, eine Taxonomie für Versionskontrollsysteme zu entwickeln. In meiner Taxonomie ist der Begriff für das, was uns hier interessiert, die Atomizität des Versionskontrollsystems. Weitere Informationen finden Sie auf Seite 22. Wenn ein VCS eine Atomizität auf Dateiebene aufweist, gibt es tatsächlich einen Verlauf für jede Datei. Das VCS muss sich den Namen der Datei und die Ereignisse an jedem Punkt merken.
Git macht das nicht. Git hat nur eine Geschichte von Commits - das Commit ist seine Einheit der Atomizität, und die Geschichte ist die Menge der Commits im Repository. Was sich ein Commit merkt, sind die Daten - ein ganzer Baum voller Dateinamen und der Inhalte, die zu jeder dieser Dateien gehören - sowie einige Metadaten: Zum Beispiel, wer das Commit durchgeführt hat, wann und warum und die interne Git-Hash-ID des übergeordneten Commits des Commits. (Es ist dieses übergeordnete Element und das gerichtete Acycling-Diagramm, das durch Lesen aller Commits und ihrer übergeordneten Elemente erstellt wurde. Dies ist der Verlauf in einem Repository.)
Beachten Sie, dass ein VCS festschreibungsorientiert sein kann und dennoch Daten Datei für Datei speichert. Das ist ein Implementierungsdetail, wenn auch manchmal ein wichtiges, und Git macht das auch nicht. Stattdessen jeder Commit Datensätze einen Baum , mit den Baum Objektcodierung Dateinamen , Betriebsart (dh, diese ausführbare Datei oder nicht?), Und ein Zeiger auf den tatsächlichen Dateiinhalt . Der Inhalt selbst wird unabhängig in einem Blob-Objekt gespeichert . Wie ein Commit-Objekt erhält ein Blob eine Hash-ID, die für seinen Inhalt eindeutig ist. Im Gegensatz zu einem Commit, das nur einmal angezeigt werden kann, kann der Blob jedoch in vielen Commits angezeigt werden. Der zugrunde liegende Dateiinhalt in Git wird also direkt als Blob und dann indirekt gespeichert in einem Baumobjekt, dessen Hash-ID (direkt oder indirekt) im Festschreibungsobjekt aufgezeichnet ist.
Wenn Sie Git bitten, Ihnen den Verlauf einer Datei anzuzeigen, indem Sie:
was Git wirklich tut das ist zu Fuß begehen Geschichte, die die einzige Geschichte Git ist, aber nicht zeigen Sie eine dieser Commits , es sei denn:
(Einige dieser Bedingungen können jedoch über zusätzliche
git log
Optionen geändert werden , und es gibt einen sehr schwer zu beschreibenden Nebeneffekt namens "History Simplification", der dazu führt, dass Git einige Commits vollständig aus dem History Walk auslässt.) Der hier angezeigte Dateiversionsverlauf ist in gewissem Sinne nicht genau im Repository vorhanden. Stattdessen handelt es sich nur um eine synthetische Teilmenge des tatsächlichen Verlaufs. Sie erhalten einen anderen "Dateiversionsverlauf", wenn Sie verschiedenegit log
Optionen verwenden!quelle
Das verwirrende Stück ist hier:
Git verwendet häufig 160-Bit-Hashes anstelle von Objekten in seinem eigenen Repo. Ein Dateibaum ist im Grunde eine Liste von Namen und Hashes, die dem jeweiligen Inhalt zugeordnet sind (plus einige Metadaten).
Der 160-Bit-Hash identifiziert den Inhalt jedoch eindeutig (innerhalb des Universums der Git-Datenbank). Ein Baum mit Hashes als Inhalt enthält also den Inhalt in seinem Zustand.
Wenn Sie den Status des Inhalts einer Datei ändern, ändert sich ihr Hash. Wenn sich jedoch der Hash ändert, ändert sich auch der mit dem Inhalt des Dateinamens verknüpfte Hash. Was wiederum den Hash des "Verzeichnisbaums" ändert.
Wenn eine Git-Datenbank einen Verzeichnisbaum speichert, impliziert und enthält dieser Verzeichnisbaum den gesamten Inhalt aller Unterverzeichnisse und aller darin enthaltenen Dateien .
Es ist in einer Baumstruktur mit (unveränderlichen, wiederverwendbaren) Zeigern auf Blobs oder andere Bäume organisiert, aber logischerweise ist es eine einzelne Momentaufnahme des gesamten Inhalts des gesamten Baums. Die Darstellung in der Git-Datenbank ist nicht der flache Dateninhalt, sondern logischerweise alle Daten und sonst nichts.
Wenn Sie den Baum in ein Dateisystem serialisiert, alle .git-Ordner gelöscht und git angewiesen haben, den Baum wieder in die Datenbank aufzunehmen, wird der Datenbank am Ende nichts hinzugefügt - das Element wäre bereits vorhanden.
Es kann hilfreich sein, sich Git-Hashes als Referenzzähler auf unveränderliche Daten vorzustellen.
Wenn Sie eine Anwendung darauf erstellt haben, besteht ein Dokument aus mehreren Seiten mit Ebenen, Gruppen und Objekten.
Wenn Sie ein Objekt ändern möchten, müssen Sie eine völlig neue Gruppe dafür erstellen. Wenn Sie eine Gruppe ändern möchten, müssen Sie eine neue Ebene erstellen, für die eine neue Seite und ein neues Dokument erforderlich ist.
Jedes Mal, wenn Sie ein einzelnes Objekt ändern, wird ein neues Dokument erstellt. Das alte Dokument bleibt bestehen. Das neue und das alte Dokument teilen den größten Teil ihres Inhalts - sie haben dieselben Seiten (außer 1). Diese eine Seite hat die gleichen Ebenen (außer 1). Diese Schicht hat die gleichen Gruppen (außer 1). Diese Gruppe hat dieselben Objekte (außer 1).
Und damit meine ich logischerweise eine Kopie, aber in Bezug auf die Implementierung ist es nur ein weiterer Zeiger mit Referenzzählung auf dasselbe unveränderliche Objekt.
Ein Git Repo ist sehr ähnlich.
Dies bedeutet, dass ein bestimmtes Git-Änderungsset seine Festschreibungsnachricht (als Hash-Code), seinen Arbeitsbaum und seine übergeordneten Änderungen enthält.
Diese übergeordneten Änderungen enthalten die übergeordneten Änderungen auf dem gesamten Rückweg.
Der Teil des Git-Repos, der die Geschichte enthält, ist diese Kette von Änderungen. Diese Änderungskette befindet sich auf einer Ebene oberhalb des "Verzeichnis" -Baums. Von einem "Verzeichnis" -Baum aus können Sie nicht eindeutig zu einem Änderungssatz und der Änderungskette gelangen.
Um herauszufinden, was mit einer Datei passiert, beginnen Sie mit dieser Datei in einem Änderungssatz. Dieser Änderungssatz hat eine Geschichte. Oft existiert in diesem Verlauf dieselbe benannte Datei, manchmal mit demselben Inhalt. Wenn der Inhalt identisch ist, wurde die Datei nicht geändert. Wenn es anders ist, gibt es eine Änderung, und es muss gearbeitet werden, um genau herauszufinden, was.
Manchmal ist die Datei weg; Der "Verzeichnis" -Baum enthält jedoch möglicherweise eine andere Datei mit demselben Inhalt (demselben Hash-Code), sodass wir ihn auf diese Weise verfolgen können (Hinweis: Aus diesem Grund möchten Sie, dass ein Commit-to-Move eine von einem Commit-to getrennte Datei verschiebt -bearbeiten). Oder der gleiche Dateiname, und nach dem Überprüfen ist die Datei ähnlich genug.
So kann git einen "Dateiverlauf" zusammenfügen.
Dieser Dateiversionsverlauf stammt jedoch aus dem effizienten Parsen des "gesamten Änderungssatzes" und nicht aus einem Link von einer Version der Datei zu einer anderen.
quelle
"git verfolgt keine Dateien" bedeutet im Grunde, dass die Commits von git aus einem Dateibaum-Snapshot bestehen, der einen Pfad im Baum mit einem "Blob" verbindet, und einem Commit-Diagramm, das den Verlauf der Commits verfolgt . Alles andere wird im laufenden Betrieb durch Befehle wie "Git Log" und "Git Blame" rekonstruiert. Diese Rekonstruktion kann über verschiedene Optionen festgelegt werden, wie schwierig es sein sollte, nach dateibasierten Änderungen zu suchen. Die Standardheuristik kann bestimmen, wann ein Blob die Position im Dateibaum ohne Änderung ändert oder wann eine Datei einem anderen Blob als zuvor zugeordnet ist. Die von Git verwendeten Komprimierungsmechanismen kümmern sich nicht viel um Blob- / Dateigrenzen. Wenn sich der Inhalt bereits irgendwo befindet, wird das Repository-Wachstum dadurch klein gehalten, ohne dass die verschiedenen Blobs zugeordnet werden.
Das ist das Repository. Git hat auch einen Arbeitsbaum, und in diesem Arbeitsbaum gibt es verfolgte und nicht verfolgte Dateien. Nur die verfolgten Dateien werden im Index aufgezeichnet (Staging-Bereich? Cache?) Und nur das, was dort verfolgt wird, gelangt in das Repository.
Der Index ist dateiorientiert und es gibt einige dateiorientierte Befehle zum Bearbeiten. Was jedoch im Repository landet, sind Commits in Form von Dateibaum-Snapshots und den zugehörigen Blob-Daten sowie den Vorfahren des Commits.
Da Git keine Dateiversionen und Umbenennungen verfolgt und deren Effizienz nicht von diesen abhängt, müssen Sie es manchmal einige Male mit verschiedenen Optionen versuchen, bis Git die Historie / Unterschiede / Schuldzuweisungen erstellt, an denen Sie für nicht triviale Historien interessiert sind.
Das ist anders bei Systemen wie Subversion, die eher aufzeichnen als rekonstruieren Historien . Wenn es nicht aufgezeichnet ist, können Sie nichts davon hören.
Ich habe tatsächlich einmal ein Differential-Installationsprogramm erstellt, das nur Release-Bäume verglichen hat, indem ich sie in Git eingecheckt und dann ein Skript erstellt habe, das ihre Wirkung dupliziert. Da manchmal ganze Bäume verschoben wurden, führte dies zu viel kleineren Differentialinstallationsprogrammen als das Überschreiben / Löschen von allem.
quelle
Git verfolgt eine Datei nicht direkt, sondern Snapshots des Repositorys, und diese Snapshots bestehen zufällig aus Dateien.
Hier ist eine Möglichkeit, es zu betrachten.
In anderen Versionskontrollsystemen (SVN, Rational ClearCase) können Sie mit der rechten Maustaste auf eine Datei klicken und deren Änderungsverlauf abrufen .
In Git gibt es keinen direkten Befehl, der dies tut. Siehe diese Frage . Sie werden überrascht sein, wie viele verschiedene Antworten es gibt. Es gibt keine einfache Antwort, da Git eine Datei nicht einfach nachverfolgt , nicht so, wie es SVN oder ClearCase tun.
quelle
git log
oder ein darauf aufbauendes Programm (oder einen Alias, der dasselbe tut). Aber selbst wenn es viele verschiedene Möglichkeiten gäbe, wie Joe sagt, gilt dies auch für die Darstellung der Branchengeschichte. (git log -p <file>
ist auch eingebaut und macht genau das)Das Verfolgen von "Inhalten" führte übrigens dazu, dass leere Verzeichnisse nicht verfolgt wurden.
Wenn Sie die letzte Datei eines Ordners eingeben, wird der Ordner selbst gelöscht .
Dies war nicht immer der Fall, und nur Git 1.4 (Mai 2006) hat diese Richtlinie zum Verfolgen von Inhalten mit Commit 443f833 durchgesetzt :
Dies wurde Jahre später im Januar 2011 mit Commit 8fe533 , Git v1.7.4 wiederholt:
In der Zwischenzeit beginnt Git mit Git 1.4.3 (Sept. 2006), nicht verfolgten Inhalt mit Commit 2074cb0 auf nicht leere Ordner zu beschränken :
Das Verfolgen von Inhalten hat es Git erlaubt, sehr früh (Git 1.4.4, Okt. 2006, Commit cee7f24 ) die Schuld zu geben :
Dies (Tracking-Inhalt) ist auch das, was git in die Git-API mit Git 1.5.0 (Dez. 2006, Commit 366bfcb ) einfügt.
Das ist , was gemacht
git add --interactive
möglich, mit der gleichen Git 1.5.0 ( commit 5cde71d )Aus diesem Grund müssen Sie zum rekursiven Entfernen aller Inhalte aus einem Verzeichnis die
-r
Option übergeben, nicht nur den Verzeichnisnamen als<path>
(immer noch Git 1.5.0, Commit 9f95069 ).Das Anzeigen des Dateiinhalts anstelle der Datei selbst ermöglicht ein Zusammenführungsszenario wie das in Commit 1de70db (Git v2.18.0-rc0, April 2018) beschriebene.
Commit 37b65ce , Git v2.21.0-rc0, Dez. 2018, hat kürzlich die Lösung kollidierender Konflikte verbessert.
Und bbafc9c begehen firther verdeutlicht die Bedeutung Datei unter Berücksichtigung Inhalte , durch die Handhabung zu verbessern für Umbenennungs / Umbenennungs (2to1) Konflikte:
quelle