Quando o Git comprime arquivos, ele faz isso usando pack files, que são coleções de objetos compactados em um único arquivo.
Uma diferença fundamental entre Mercurial e Git é o uso do armazenamento em disco. Mercurial usa um modelo de armazenamento por arquivo, no qual todo o histórico sobre esse arquivo é armazenado dentro dessa unidade. Isso é similar ao uso do CVS de arquivos ,v para armazenar informações de versão sobre um arquivo específico, embora o formato do Mercurial seja muito melhor sintonizado. Git, por outro lado, tem um modelo de objeto lógico, e se esses objetos estão em um pack file compactado ou ‘soltos’ no disco, não faz diferença para o Git.
Isso permite que o Git empacote novamente os objetos, e gere novos pack files, posteriores ao início da sua criação. Enquanto o objeto estiver disponível, realmente não interessa de onde ele veio – afinal, o hash exclusivo irá sempre apontar para o mesmo conteúdo no objeto, independentemente de onde ele for carregado. Claro, uma vez que um pack file é imutável após a criação, todas as mudanças são implementadas com a criação de pack files novos e, em seguida, com o descarte dos antigos.
Uma vez que os pack files contêm muitos objetos, é possível realizar a compressão delta contra os objetos. Em outras palavras, se existem cinco versões de um arquivo, todas em grande parte o mesmo mas com pequenas diferenças, então uma versão inicial do arquivo pode ser armazenada como é, e apenas as modificações menores armazenadas depois. (Estas geralmente incluem uma inserção de uma série de bytes, a exclusão de uma série ou uma transposição de uma série de bytes.) Assim, embora logicamente o pack file possa conter vários objetos, na verdade ele só armazena um conjunto de cada mudança.
Existem várias maneiras de armazenar arquivos no pack file. Por exemplo, considere um arquivo com o conteúdo ‘A’ e uma versão posterior ‘AB’. Ele pode ser armazenado como um A e, em seguida, +B, ou pode ser armazenado como AB e -B. O último tem um desempenho melhor, uma vez que o conjunto de alterações envolve uma deleção de um intervalo existente, portanto, apenas precisa armazenar o início e o comprimento da exclusão ocorrendo. Como os arquivos aumentam ao longo do tempo, isso também significa que o maior arquivo é normalmente armazenado como está (geralmente, o mais recente), que dá acesso mais rápido para isso do que em versões anteriores.
Clones e Fetches
A outra vez em que os pack files são criados é quando você clona ou faz um fetch/pull de um repositório Git. O protocolo “http inteligente”, na verdade usa o protocolo Git por meio de uma conexão HTTP empacotada; o que acontece é que há um pouco de vai-e-vem ao decidir quais hashes você tem, e o que você quer. Em última análise, essa conversa resulta no servidor sabendo que conjuntos de objetos serão enviados ao cliente.
O mecanismo mais eficiente para o envio de resultados de volta para o cliente é na forma de um pack file. O servidor sabe o que enviar de volta, então ele cria um pack file contendo apenas esses objetos. Isso envolverá a compressão (a compressão delta e posteriormente um deflate) e, em seguida, enviará os objetos de volta. Você mesmo verá isso na conversa que será enviada no canal de mensagem quando clonar ou atualizar o repositório:
(master) $ git pull
remote: Counting objects: 1177, done.
remote: Compressing objects: 100% (352/352), done.
remote: Total 1018 (delta 609), reused 787 (delta 390)
Receiving objects: 100% (1018/1018), 378.46 KiB | 206 KiB/s, done.
Resolving deltas: 100% (609/609), completed with 110 local objects.
A primeira linha é o servidor contando o número de objetos (blobs, trees, commits etc.), que são necessários como parte do conjunto de objeto. De certa forma, isso está fazendo o mesmo tipo de trabalho que o git rev-list poderia fazer; em outras palavras, está descobrindo o conjunto de objetos necessários para enviar.
A segundas e a terceiras linhas são o servidor dizendo que está agora comprimindo esses objetos em um pack file. Isso é feito do lado do servidor antes que ele possa começar a enviar os dados, já que o pack file em si não é escrito em um formato de streaming. Além disso, ele pode escrever tanto objetos completos ou objetos delta. (Normalmente, um pack file é auto-suficiente, ou seja, ele só vai criar deltas contra outros objetos que estão no mesmo pack file. Para os clones, o pack file pode armazenar referências a objetos que o cliente já tem, mas sem copiar o objeto no pack file em si.)
A quarta linha é o client recebendo o pack file do servidor, uma vez que o servidor o criou. Os metadados no pack file dão uma indicação de quão longe ele foi, razão pela qual você pode obter uma atualização que ocorre enquanto o arquivo é recebido.
A quinta e última linha é o client montando os dados e fechando qualquer um dos loops que podem estar presentes (isto é, referências locais a outros objetos). Ele também irá gerar um arquivo de índice dos objetos, uma vez que pode ser calculado com base no hash SHA1 dos próprios objetos.
Tudo isso junto significa que quando você clona um projeto pela primeira vez, você acaba com um grande pack file que contém tudo. As atualizações subsequentes pode derrubar os pack files menores, que podem se acumular nos arquivos anteriores. A qualquer momento (ou quando você executar git gc manualmente), esses pack files podem ser reorganizados em unidades mais adequadas de armazenamento; por exemplo, condensando vários pack files em um ou em alguns poucos pack files. Uma vez que essa operação é persistente (isto é, o pack file grande irá reter o seu comportamento de compressão), pode ser benéfica para comprimir um repositório periodicamente com git gc –agressive.
Resumo
O pack file do Git é a unidade que torna o armazenamento de objetos eficiente, e também suporta a transferência eficiente de dados entre o cliente e o servidor durante um clone inicial e em uma operação de fetch/pull subsequente. Embora pack files sejam imutáveis uma vez que são criados, eles podem ser recriados com um conteúdo mais comprimido se necessário.
?
Texto original disponível em http://alblue.bandlem.com/2011/09/git-tip-of-week-packfiles-redux.html







