O projeto open source Git acaba de lançar o Git 2.49, trazendo diversas novidades e correções de bugs com a colaboração de mais de 89 pessoas, sendo 24 delas novos contribuintes. Esta atualização foca em melhorias de desempenho e novas funcionalidades que beneficiam tanto desenvolvedores quanto usuários do Git. Vamos explorar os principais destaques do Git 2.49 e o que ele oferece de novo.
## Empacotamento Mais Rápido com Name Hash v2
Uma das grandes novidades do Git 2.49 é a otimização no empacotamento de objetos, utilizando o name hash v2. Para entender melhor, o Git armazena objetos individualmente (objetos “soltos”) ou agrupados em packfiles. Os packfiles são usados em diversas funções, como armazenamento local e transferência de dados entre repositórios Git.
Armazenar objetos em packfiles oferece várias vantagens em relação ao armazenamento individual. A principal delas é a rapidez nas buscas de objetos. Ao procurar um objeto solto, o Git precisa realizar várias chamadas ao sistema para encontrar, abrir, ler e fechar o objeto. Essas chamadas podem ser aceleradas com o block cache do sistema operacional, mas o acesso pseudo-aleatório dificulta a eficiência do cache.
Objetos soltos são compactados individualmente, impedindo o uso de representações delta de outros objetos semelhantes. Imagine que você está fazendo pequenas alterações em um blob grande no seu repositório. Inicialmente, cada objeto é armazenado individualmente e compactado com zlib. Se a maior parte do conteúdo permanece igual, o Git pode compactar versões sucessivas como deltas das anteriores, armazenando apenas as mudanças em vez de múltiplas cópias quase idênticas.
Para identificar bons candidatos para armazenamento delta, o Git compara objetos que aparecem em caminhos similares, usando um “name hash“. Essa função, criada por Linus em 2006, calcula um hash numérico que prioriza os 16 caracteres finais não-espaço em um caminho de arquivo, agrupando funções com extensões similares (.c
, .h
) ou arquivos movidos entre diretórios (a/foo.txt
para b/foo.txt
).
A implementação atual do name hash pode resultar em baixa compressão quando muitos arquivos compartilham o mesmo nome base, mas possuem conteúdos diferentes, como vários arquivos CHANGELOG.md
para diferentes subsistemas. O Git 2.49 introduz uma nova versão da função hash que considera mais a estrutura do diretório. Cada nível da hierarquia de diretórios recebe seu próprio hash, que é deslocado e combinado com XOR no hash geral, tornando-o mais sensível ao caminho completo.
Essa nova abordagem pode melhorar significativamente tanto o desempenho do empacotamento quanto o tamanho dos packfiles resultantes. Por exemplo, ao usar a nova função hash, o tempo para reempacotar microsoft/fluentui
caiu de aproximadamente 96 segundos para 34 segundos, e o tamanho do pack resultante diminuiu de 439 MiB para 160 MiB.
Embora essa funcionalidade ainda não seja compatível com os reachability bitmaps do Git, você pode testá-la utilizando a nova flag --name-hash-version
nos comandos git repack
ou git pack-objects
na versão mais recente.
## Preenchimento de Blobs Históricos em Clones Parciais
Já se deparou com a seguinte mensagem ao trabalhar em um clone parcial?
$ git blame README.md
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 1.64 KiB | 8.10 MiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 1.64 KiB | 7.30 MiB/s, done.
[...]
Isso ocorre em clones parciais criados com --filter=blob:none
. Nesses casos, o repositório contém todas as trees, commits e tags anotadas, mas apenas os blobs diretamente acessíveis a partir do HEAD. Ou seja, o clone local possui os blobs necessários para um checkout completo na revisão mais recente, e o carregamento de qualquer blob histórico buscará os objetos ausentes no repositório de origem.
No exemplo acima, solicitamos um blame
do arquivo README.md
. Para construir esse blame, precisamos ver cada versão histórica do arquivo para calcular o diff em cada camada e determinar se uma revisão modificou uma linha específica. No entanto, o Git carrega cada versão histórica do objeto individualmente, resultando em armazenamento inchado e baixo desempenho.
O Git 2.49 introduz uma nova ferramenta, git backfill
, que pode preencher os blobs históricos ausentes de um clone --filter=blob:none
em pequenos lotes. Essas requisições utilizam a nova API path-walk (também introduzida no Git 2.49) para agrupar objetos que aparecem no mesmo caminho, melhorando a compressão delta nos packfiles enviados pelo servidor. Como as requisições são enviadas em lotes, podemos preencher todos os blobs ausentes em poucos packs, em vez de um pack por blob.
Após executar git backfill
no exemplo acima, a experiência se torna mais eficiente:
$ git clone --sparse --filter=blob:none git@github.com:git/git.git
[...] # downloads historical commits/trees/tags
$ cd git
$ git sparse-checkout add builtin
[...] # downloads current contents of builtin/
$ git backfill --sparse
[...] # backfills historical contents of builtin/
$ git blame -- builtin/backfill.c
85127bcdeab (Derrick Stolee 2025-02-03 17:11:07 +0000 1) /* We need this macro to access core_apply_sparse_checkout */
85127bcdeab (Derrick Stolee 2025-02-03 17:11:07 +0000 2) #define USE_THE_REPOSITORY_VARIABLE
85127bcdeab (Derrick Stolee 2025-02-03 17:11:07 +0000 3)
[...]
Executar git backfill
logo após clonar um repositório com --filter=blob:none
não traz muitos benefícios, pois seria mais simples clonar o repositório sem o filtro de objetos. Ao usar a opção --sparse
do comando backfill (o padrão quando o recurso sparse checkout está habilitado), o Git baixa apenas os blobs que aparecem dentro do seu sparse checkout, evitando objetos que você não faria checkout de qualquer maneira.
Para experimentar, execute git backfill
em qualquer clone --filter=blob:none
de um repositório usando o Git 2.49 hoje mesmo!
## Melhorias e Otimizações Adicionais
Além das grandes novidades, o Git 2.49 traz outras melhorias e otimizações que merecem destaque:
* Integração com zlib-ng: O Git agora pode ser compilado com zlib-ng, uma versão otimizada da biblioteca zlib, que oferece melhor desempenho na compressão de objetos. Testes experimentais mostram um aumento de velocidade de cerca de 25% ao imprimir o conteúdo de todos os objetos no repositório Git.
* Primeiros passos com Rust: Esta versão marca a inclusão dos primeiros códigos em Rust no projeto Git, com a introdução de duas crates: libgit-sys e libgit, que são wrappers de baixo e alto nível para uma pequena parte do código da biblioteca Git.
* Avanços na “libificação“: O projeto continua a evoluir para tornar o Git mais modular e utilizável como uma biblioteca independente. Várias mudanças foram feitas para substituir variáveis globais e eliminar avisos de comparação de sinais, contribuindo para um código mais limpo e seguro.
* Melhorias no git gc: O comando git gc, que realiza a limpeza e otimização do repositório, agora expõe a opção --expire-to
, permitindo mover objetos inacessíveis para um local separado em vez de excluí-los permanentemente.
* Correção no help.autocorrect: A opção help.autocorrect, que sugere correções para comandos Git digitados incorretamente, teve seu comportamento alterado para seguir a convenção de outras opções booleanas, evitando execuções acidentais de comandos corrigidos.
* Nova opção –revision no git clone: Agora é possível clonar o histórico de um repositório até uma revisão específica, mesmo que ela não esteja associada a uma branch ou tag, utilizando a opção --revision
no comando git clone.
* Remoção de suporte a formatos de remotes antigos: O Git 3.0 removerá o suporte a formatos de remotes antigos, que utilizavam arquivos em $GIT_DIR/branches
ou $GIT_DIR/remote
.
Para saber mais sobre as mudanças que quebrarão a compatibilidade, você pode consultar o arquivo Documentation/BreakingChanges.adoc
. Se você realmente quiser viver no limite, pode compilar o Git com a opção WITH_BREAKING_CHANGES
, que remove os recursos que serão eliminados no Git 3.0.
O lançamento do Git 2.49 é um marco importante, com foco em desempenho, novas funcionalidades e melhorias na estrutura interna do projeto. As novidades beneficiam tanto desenvolvedores quanto usuários do Git, tornando o trabalho com versionamento de código mais eficiente e agradável.
Este conteúdo foi auxiliado por Inteligência Artificial, mas escrito e revisado por um humano.
Via GitHub Blog