Novidades da versão 2.49 do Git

O projeto open source Git acaba de lançar o Git 2.49, trazendo consigo diversas novidades e correções de bugs provenientes de mais de 89 colaboradores, sendo 24 deles estreantes. Esta atualização promete otimizar o trabalho dos desenvolvedores e aprimorar a experiência com a ferramenta. Vamos explorar os Destaques do Git 2.49 e entender como essas mudanças impactam o dia a dia dos usuários.

## Empacotamento Acelerado com Name-Hash v2

Já abordamos em outras ocasiões o modelo de armazenamento de objetos do Git, onde os objetos podem ser escritos individualmente (conhecidos como objetos “soltos”) ou agrupados em packfiles. O Git utiliza packfiles em diversas funções, incluindo armazenamento local e na transferência de dados entre repositórios Git.

Armazenar objetos em packfiles oferece vantagens em relação ao armazenamento individual. Uma delas é a rapidez nas buscas de objetos. Ao procurar um objeto solto, o Git precisa fazer várias chamadas ao sistema para encontrá-lo, abri-lo, lê-lo e fechá-lo. Essas chamadas podem ser agilizadas com o cache de bloco do sistema operacional, mas o acesso pseudoaleatório dificulta o uso eficiente do cache.

Objetos soltos são comprimidos isoladamente, impedindo o armazenamento de objetos como deltas de outros objetos semelhantes. Por exemplo, ao fazer pequenas alterações em um blob grande, cada objeto é armazenado individualmente e comprimido com zlib. No entanto, se grande parte do conteúdo permanece inalterada, o Git pode comprimir versões sucessivas como deltas de versões anteriores, armazenando apenas as mudanças.

Para identificar bons candidatos para delta-base pairs, 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 considera os 16 caracteres finais não-espaço em um caminho de arquivo, agrupando funções com extensões similares ou arquivos movidos entre diretórios.

A implementação atual do name-hash pode gerar compressão ineficiente quando muitos arquivos compartilham o mesmo nome base, mas têm conteúdos diferentes. Para solucionar este problema, o Git 2.49 introduz uma nova variante 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 um XOR no hash geral, tornando a função mais sensível ao caminho completo.

Essa melhoria pode impulsionar tanto o desempenho do empacotamento quanto o tamanho final do pack. Por exemplo, a nova função hash reduziu o tempo de reempacotamento de microsoft/fluentui de 96 para 34 segundos, e o tamanho do pack resultante de 439 MiB para 160 MiB.

Embora essa funcionalidade ainda não seja compatível com os reachability bitmaps do Git, você pode experimentá-la usando a flag --name-hash-version no git repack ou git pack-objects.

## Preenchimento de Blobs Históricos em Clones Parciais

Quem nunca se deparou com uma saída inesperada 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.
[…]
“`

Para entender o que aconteceu, vamos analisar um cenário.

Imagine que você está trabalhando em um clone parcial criado com --filter=blob:none. Seu repositório terá todas as trees, commits e objetos de tag anotados, mas apenas os blobs imediatamente acessíveis a partir do HEAD. Em outras palavras, seu clone local terá os blobs necessários para um checkout completo na revisão mais recente, e qualquer blob histórico ausente será carregado do repositório original.

No exemplo acima, solicitamos um blame do arquivo README.md. Para construir esse blame, precisamos ver todas as versões históricas 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 inchaço do armazenamento e baixo desempenho.

O Git 2.49 introduz uma nova ferramenta, git backfill, que pode preencher blobs históricos ausentes de um clone --filter=blob:none em pequenos lotes. Essas solicitações usam a nova API path-walk (também introduzida no Git 2.49) para agrupar objetos que aparecem no mesmo caminho, resultando em melhor compressão delta nos packfiles enviados pelo servidor. Como as solicitações são enviadas em lotes, podemos preencher todos os blobs ausentes em poucos packs.

Após executar git backfill, a experiência se torna mais eficiente:

“`
$ git clone –sparse –filter=blob:none [email protected]: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 grandes benefícios, pois seria mais simples clonar sem filtro de objeto. Ao usar a opção --sparse do comando backfill (padrão quando o recurso sparse checkout está habilitado), o Git baixa apenas os blobs dentro do sparse checkout, evitando objetos que não seriam extraídos de qualquer forma.

Para experimentar, execute git backfill em qualquer clone --filter=blob:none usando o Git 2.49!

## Outras Atualizações e Melhorias no Git 2.49

Além das funcionalidades mencionadas, o Git 2.49 traz outras novidades interessantes:

* Otimização com zlib-ng: O Git utiliza compressão zlib para escrever objetos soltos e dentro de packs. O zlib-ng é um fork que incorpora otimizações e remove códigos obsoletos do zlib original, com foco em desempenho. Ele oferece suporte a conjuntos de instruções SIMD, como SSE2 e AVX2. No Git 2.49, é possível compilar o Git com zlib-ng, resultando em ganhos de velocidade de cerca de 25% em algumas operações.

* Integração com Rust: Esta versão marca um passo importante com a inclusão de código em Rust no projeto Git. Foram introduzidas duas crates Rust: libgit-sys e libgit, que são wrappers de baixo e alto nível para uma pequena parte do código da biblioteca Git. O projeto Git tem trabalhado para tornar seu código mais orientado a bibliotecas, e esta versão aproveita esse esforço para fornecer um crate Rust que envolve parte da API config.h do Git.

* Esforços de “libificação: Houve outras mudanças relacionadas ao esforço de “libificação“. O projeto continua a se afastar de variáveis globais, e muitos comandos agora usam o repository fornecido em vez do global.

* Correção de warnings: A equipe também se dedicou a eliminar warnings -Wsign-compare, que ocorrem quando um valor com sinal é comparado a um valor sem sinal.

* Opção --expire-to no git gc: A opção --expire-to no git repack controla o comportamento de objetos inacessíveis que foram removidos do repositório. No Git 2.49, essa opção também está disponível no git gc através do comando git gc --expire-to.

* Melhoria no help.autocorrect: A funcionalidade de autocorrect do Git agora interpreta o valor “1” para help.autocorrect como outros comandos booleanos, e números positivos maiores que 1 como antes.

* Opção --revision no git clone: Agora é possível clonar um repositório até uma revisão específica, mesmo que ela não esteja em nenhum branch ou tag, usando a opção --revision.

* Remoção de suporte a configurações antigas de remotes: O Git está removendo o suporte a mecanismos antigos de configuração de remotes baseados em arquivos em $GIT_DIR/branches e $GIT_DIR/remote. Essas funcionalidades serão removidas no Git 3.0.

* Estágio de dois Outreachy interns: O projeto Git recebeu dois estagiários do Outreachy que concluíram seus projetos com sucesso. Usman Akinyemi adicionou suporte para incluir informações uname no user agent do Git ao fazer requisições HTTP, e Seyi Kuforiji converteu mais testes de unidade para usar o Clar testing framework.

Este é apenas um resumo das mudanças presentes na versão mais recente. Para mais detalhes, consulte as notas de lançamento do Git 2.49.

Este conteúdo foi auxiliado por Inteligência Artificiado, mas escrito e revisado por um humano.

Via The GitHub Blog

Leave a Comment