O projeto open source Git lançou a versão 2.49, com novidades e correções de bugs feitas por mais de 89 colaboradores, sendo 24 deles novos. Para celebrar este lançamento, o GitHub apresenta os destaques do Git 2.49, com as funcionalidades e mudanças mais interessantes desde a última atualização.
Um dos principais objetivos desta versão é otimizar o armazenamento e a compressão de objetos, tornando o Git mais rápido e eficiente. Além disso, há melhorias na experiência de clones parciais e na compatibilidade com outras ferramentas.
Empacotamento Mais Rápido com Name Hash v2
Objetos no Git podem ser armazenados individualmente (objetos “soltos”) ou agrupados em packfiles. O Git usa packfiles em diversas funções, como armazenamento local e transferência de dados entre repositórios.
Armazenar objetos em packfiles oferece vantagens em relação ao armazenamento individual. A busca por objetos é mais rápida no armazenamento em packfiles, pois o Git precisa fazer várias chamadas de sistema para encontrar, abrir, ler e fechar um objeto solto.
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 versão é armazenada individualmente e comprimida com zlib. Se grande parte do conteúdo do arquivo permanece inalterada, o Git pode comprimir as versões sucessivas como deltas das anteriores, armazenando apenas as mudanças em vez de cópias quase idênticas.
Como o Git Identifica Pares de Objetos para Compressão Delta
O Git compara objetos que aparecem em paths similares, usando um “name hash“, um hash numérico que prioriza os 16 caracteres finais não-espaço em um filepath. Essa função, criada por Linus em 2006, agrupa funções com extensões similares (.c
, .h
) ou arquivos movidos de um diretório para outro (a/foo.txt
para b/foo.txt
).
A implementação atual do name hash pode gerar compressão ruim quando há muitos arquivos com o mesmo nome base, mas 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 ao calcular o hash.
Cada nível da hierarquia de diretórios recebe seu próprio hash, que é deslocado para baixo e combinado com um XOR no hash geral. Isso cria uma função hash mais sensível ao path completo, não apenas aos 16 caracteres finais.
Essa mudança traz melhorias no desempenho do empacotamento e no tamanho do pack resultante. Por exemplo, a nova função hash reduziu o tempo de reempacotamento de microsoft/fluentui
de ~96 segundos para ~34 segundos, e o tamanho do pack 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 com a flag --name-hash-version
nos comandos git repack
ou git pack-objects
.
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 trees, commits e objetos de tag anotados, mas apenas os blobs diretamente acessíveis a partir do HEAD. Ou seja, o clone local tem apenas os blobs necessários para um checkout completo na revisão mais recente, e carregar blobs históricos acionará o carregamento de objetos ausentes do repositório original.
No exemplo acima, o comando blame
do arquivo README.md
exige todas as versões históricas do arquivo para calcular o diff e determinar se uma revisão modificou uma linha. O Git carrega cada versão histórica do objeto individualmente, resultando em armazenamento inflado e baixo desempenho.
A Nova Ferramenta Git Backfill
O Git 2.49 introduz o git backfill
, que carrega blobs históricos ausentes de um clone --filter=blob:none
em pequenos lotes. As requisições usam a nova API path-walk (também do Git 2.49) para agrupar objetos que aparecem no mesmo path, otimizando a compressão delta nos packfiles enviados pelo servidor. Como as requisições são enviadas em lotes, é possível preencher todos os blobs ausentes em poucos packs, em vez de um pack por blob.
Após usar git backfill
, a experiência se torna:
$ 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 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 no sparse checkout, evitando objetos que não seriam extraídos de qualquer forma. Experimente o git backfill
em qualquer clone --filter=blob:none
usando o Git 2.49!
Outras Melhorias e Novidades
Além das grandes mudanças discutidas acima, o Git 2.49 também traz outras novidades interessantes:
* Otimização com zlib-ng: O Git usa compressão zlib ao gravar objetos soltos ou dentro de packs. O zlib-ng é um fork do zlib que inclui otimizações, remove código desnecessário e prioriza o desempenho. O zlib-ng tem suporte para conjuntos de instruções SIMD (como SSE2 e AVX2). No Git 2.49, você pode compilar o Git com zlib-ng passando ZLIB_NG
ao compilar com o GNU Make, ou a opção zlib_backend
ao compilar com Meson. Testes experimentais mostram um aumento de velocidade de ~25% ao imprimir o conteúdo de todos os objetos no repositório Git (de ~52.1 segundos para ~40.3 segundos).
* Primeiros Passos com Rust: O lançamento marca um marco importante para o projeto Git com a inclusão dos primeiros códigos em Rust. Especificamente, a versão introduz duas crates Rust: libgit-sys e libgit, que são wrappers de baixo e alto nível em torno de uma pequena parte do código da biblioteca do Git. O projeto Git está evoluindo seu código para ser mais orientado a biblioteca, substituindo funções que encerram o programa por funções que retornam um inteiro e permitem que o chamador decida se deve sair, além de corrigir vazamentos de memória. Esta versão aproveita esse trabalho para fornecer uma crate Rust de prova de conceito 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 de abandonar variáveis globais como the_repository
continua, e muitos comandos nesta versão usam o repository
fornecido em vez do global.
* Correção de Avisos -Wsign-compare
: Esta versão também teve um grande esforço para eliminar avisos -Wsign-compare
, que ocorrem quando um valor com sinal é comparado a um valor sem sinal. Isso pode levar a um comportamento inesperado quando comparamos valores negativos com sinal a valores sem sinal, onde uma comparação como -1 < 2
(que deveria retornar verdadeiro) acaba retornando falso.
* 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. Por padrão, objetos removidos são simplesmente deletados, mas --expire-to
permite movê-los para o lado caso você queira mantê-los para fins de backup. No Git 2.49, você pode experimentar esse comportamento via git gc --expire-to
.
* Melhorias no help.autocorrect
: O Git 2.49 muda a convenção de help.autocorrect
para interpretar "1" como outros comandos com valor booleano, e números positivos maiores que 1 como antes. Embora você não possa mais especificar que deseja o comportamento de autocorreção em exatamente 1 decissegundo, provavelmente nunca quis fazer isso de qualquer maneira.
* Nova Opção --revision
no git clone
: O Git 2.49 introduz um método mais conveniente para complementar as opções --branch
e --tag
, adicionando uma nova opção --revision
que busca o histórico que leva à revisão especificada, independentemente de haver ou não um branch ou tag apontando para ela.
* Remoção de Mecanismos Antigos de Remotos: Nos primórdios do Git, os remotos eram configurados via arquivos separados em $GIT_DIR/branches
. Semanas depois, a convenção mudou para usar $GIT_DIR/remote
. Ambas as convenções foram substituídas pelo mecanismo baseado em configuração que conhecemos hoje. Quando o Git 3.0 for lançado, esses recursos serão removidos.
* Estagiários do Outreachy: O projeto Git teve dois estagiários do Outreachy que concluíram seus projetos! Usman Akinyemi trabalhou na adição de suporte para incluir informações do uname no user agent do Git ao fazer requisições HTTP, e Seyi Kuforiji trabalhou na conversão de mais testes de unidade para usar o Clar testing framework.
Essas são apenas algumas das atualizações do Git 2.49. Para mais detalhes, consulte as notas de lançamento.
Este conteúdo foi auxiliado por Inteligência Artificial, mas escrito e revisado por um humano.
Via The GitHub Blog