A parte confusa está aqui:
O Git nunca os vê como arquivos individuais. O Git pensa em tudo como conteúdo completo.
O Git geralmente usa hashes de 160 bits no lugar de objetos em seu próprio repositório. Uma árvore de arquivos é basicamente uma lista de nomes e hashes associados ao conteúdo de cada um (mais alguns metadados).
Mas o hash de 160 bits identifica exclusivamente o conteúdo (dentro do universo do banco de dados git). Portanto, uma árvore com hashes como conteúdo inclui o conteúdo em seu estado.
Se você alterar o estado do conteúdo de um arquivo, seu hash será alterado. Mas se o hash for alterado, o hash associado ao conteúdo do nome do arquivo também será alterado. O que, por sua vez, altera o hash da "árvore de diretórios".
Quando um banco de dados git armazena uma árvore de diretórios, essa árvore de diretórios implica e inclui todo o conteúdo de todos os subdiretórios e todos os arquivos nele .
Ele é organizado em uma estrutura de árvore com ponteiros (imutáveis, reutilizáveis) para blobs ou outras árvores, mas logicamente é um instantâneo único de todo o conteúdo de toda a árvore. A representação no banco de dados git não é o conteúdo de dados simples, mas logicamente são todos os dados e nada mais.
Se você serializou a árvore em um sistema de arquivos, excluiu todas as pastas .git e disse ao git para adicionar a árvore novamente ao banco de dados, você acabaria adicionando nada ao banco de dados - o elemento já estaria lá.
Pode ajudar pensar nos hashes do git como um ponteiro de referência contado para dados imutáveis.
Se você criou um aplicativo em torno disso, um documento é um monte de páginas, com camadas, grupos e objetos.
Quando você deseja alterar um objeto, é necessário criar um grupo completamente novo para ele. Se você deseja alterar um grupo, é necessário criar uma nova camada, que precisa de uma nova página, que precisa de um novo documento.
Toda vez que você altera um único objeto, ele gera um novo documento. O documento antigo continua a existir. Os documentos novos e antigos compartilham a maior parte de seu conteúdo - eles têm as mesmas páginas (exceto 1). Essa página tem as mesmas camadas (exceto 1). Essa camada tem os mesmos grupos (exceto 1). Esse grupo tem os mesmos objetos (exceto 1).
E, do mesmo modo, quero dizer logicamente uma cópia, mas em termos de implementação é apenas mais um ponteiro de referência contado para o mesmo objeto imutável.
Um repositório Git é muito parecido com isso.
Isso significa que um dado conjunto de alterações git contém sua mensagem de confirmação (como um código hash), sua árvore de trabalho e suas alterações pai.
Essas alterações pai contêm as alterações pai, desde o início.
A parte do repositório git que contém a história é essa cadeia de mudanças. Essa cadeia de alterações a um nível acima da árvore "diretório" - de uma árvore "diretório", você não pode acessar exclusivamente um conjunto de alterações e a cadeia de alterações.
Para descobrir o que acontece com um arquivo, você começa com esse arquivo em um conjunto de alterações. Esse changeset tem um histórico. Freqüentemente nesse histórico, existe o mesmo arquivo nomeado, às vezes com o mesmo conteúdo. Se o conteúdo for o mesmo, não houve alteração no arquivo. Se for diferente, há uma mudança e é preciso trabalhar para descobrir exatamente o que.
Às vezes o arquivo se foi; mas a árvore "diretório" pode ter outro arquivo com o mesmo conteúdo (o mesmo código de hash), para que possamos acompanhá-lo dessa maneira (observe; é por isso que você deseja um commit para mover um arquivo separado de um commit para -editar). Ou o mesmo nome de arquivo e, após verificar o arquivo, é semelhante o suficiente.
Assim, o git pode juntar um "histórico de arquivos".
Mas esse histórico de arquivo vem da análise eficiente do "conjunto de alterações inteiro", não de um link de uma versão do arquivo para outra.