La parte confusa è qui:
Git non li vede mai come singoli file. Git considera tutto come il contenuto completo.
Git usa spesso hash a 160 bit al posto degli oggetti nel proprio repository. Un albero di file è fondamentalmente un elenco di nomi e hash associati al contenuto di ciascuno (oltre ad alcuni metadati).
Ma l'hash a 160 bit identifica in modo univoco il contenuto (all'interno dell'universo del database git). Quindi un albero con hash come contenuto include il contenuto nel suo stato.
Se si modifica lo stato del contenuto di un file, il relativo hash cambia. Ma se cambia l'hash, cambia anche l'hash associato al contenuto del nome del file. Che a sua volta cambia l'hash dell '"albero delle directory".
Quando un database git memorizza un albero di directory, tale albero di directory implica e include tutto il contenuto di tutte le sottodirectory e tutti i file in essa contenuti .
È organizzato in una struttura ad albero con puntatori (immutabili, riutilizzabili) a chiazze o altri alberi, ma logicamente è una singola istantanea dell'intero contenuto dell'intero albero. La rappresentazione nel database git non è il contenuto dei dati flat, ma logicamente sono tutti i suoi dati e nient'altro.
Se si serializzava l'albero in un filesystem, si cancellavano tutte le cartelle .git e si dicesse a git di aggiungere nuovamente l'albero nel suo database, si finirebbe per aggiungere nulla al database: l'elemento sarebbe già lì.
Potrebbe essere utile pensare agli hash di Git come un puntatore contato di riferimento a dati immutabili.
Se hai creato un'applicazione attorno a questo, un documento è un insieme di pagine, che hanno livelli, che hanno gruppi, che hanno oggetti.
Quando vuoi cambiare un oggetto, devi creare un gruppo completamente nuovo per esso. Se vuoi cambiare un gruppo, devi creare un nuovo livello, che ha bisogno di una nuova pagina, che ha bisogno di un nuovo documento.
Ogni volta che cambi un singolo oggetto, genera un nuovo documento. Il vecchio documento continua ad esistere. Il nuovo e vecchio documento condividono la maggior parte dei loro contenuti: hanno le stesse pagine (tranne 1). Quella pagina ha gli stessi livelli (tranne 1). Quel layer ha gli stessi gruppi (tranne 1). Quel gruppo ha gli stessi oggetti (tranne 1).
E allo stesso modo, intendo logicamente una copia, ma dal punto di vista dell'implementazione è solo un altro riferimento al puntatore dello stesso oggetto immutabile.
Un repository git è molto simile.
Ciò significa che un determinato changeset git contiene il suo messaggio di commit (come codice hash), contiene il suo albero di lavoro e contiene le sue modifiche padre.
Le modifiche padre contengono le modifiche padre, tutte indietro.
La parte del repository git che contiene la cronologia è quella catena di modifiche. Quella catena di modifiche lo modifica a un livello sopra l'albero "directory" - da un albero "directory", non è possibile accedere in modo univoco a una serie di modifiche e alla catena di modifiche.
Per scoprire cosa succede a un file, si inizia con quel file in un changeset. Quel changeset ha una storia. Spesso in quella cronologia, esiste lo stesso file denominato, a volte con lo stesso contenuto. Se il contenuto è lo stesso, non è stato apportato alcun cambiamento al file. Se è diverso, c'è un cambiamento e il lavoro deve essere fatto per capire esattamente cosa.
A volte il file è sparito; ma l'albero "directory" potrebbe avere un altro file con lo stesso contenuto (stesso codice hash), quindi possiamo seguirlo in quel modo (nota; ecco perché vuoi che un commit commuova un file separato da un commit-to -modificare). O lo stesso nome file e dopo aver verificato il file è abbastanza simile.
Quindi git può patchwork insieme una "cronologia dei file".
Ma questa cronologia dei file deriva dall'analisi efficiente dell'intero "changeset", non da un collegamento da una versione del file a un'altra.