Desenvolvimento

24 ago, 2012

Dica Git da Semana: GC e Pruning

Publicidade

Paul Webster escreveu “Where the git did that go?” em relação ao incrível desaparecimento de commits. Alguns disseram que um sistema de controle de versão não deve ser capaz de fazer isso, mas na verdade  tudo faz parte da funcionalidade do Git. Vejamos o que aconteceu.

Um repositório Git armazena commits em uma closure transitiva (uma closure real, não um lambda) a partir dos itens acessados através de cada commit (e cada tree e blob). Não é possível remover um commit – e, portanto, as trees e os blobs que compõem esse repositório.

Então, como é possível perder dados com Git? Bem, se você estiver usando um repositório Git padrão, você pode criar branches com git branch, e excluí-los com git branch-d. Quando você exclui um branch, você remove o ponteiro para o último commit – mas na verdade você não os perde.

Além disso, o git reflog, que abordamos anteriormente, armazena uma lista dos ponteiros do branch anterior. Em outras palavras, mesmo se você excluir um branch, o reflog te defende.

No entanto, de um modo geral, apenas repositórios com diretórios em funcionamento possuem reflogs; poucos repositórios tendem a não ter. Há uma opção de configuração, git config core.logAllRefUpdates, que pode ser usada para forçá-lo em todos os repositórios – ou desativá-lo completamente caso não seja necessário.

Mesmo sem um reflog, os commits não são removidos imediatamente. Se você executar um git gc, que empacota novamente o repositório para uma estrutura mais eficiente, ele vai exportar commits não referenciados como objetos soltos. (Você tem que garantir que não existem quaisquer branches ou tags ou reflogs para ver esse comportamento; se há um ponteiro existente, então ele não irá expulsar o objeto do packfile.)

Executar um git fsck vai verificar que todos os objetos estão presentes como esperado. Você também pode visualizar o que não é mais referenciado; executar git fsck –unreachable vai te mostrar quais commits não estão mais acessíveis devido aos branches eliminados ou às tags removidas. Executar git fsck –unreachable diariamente e relatórios diários  vai te dar um bom alerta do commit que está em vias de extinção, caso isso seja uma preocupação.

Os objetos que não são mais referenciados podem ser removidos com git prune; embora essa seja uma operação de nível inferior, que muitas vezes é chamada de git gc. Por padrão, ela não removerá commits mais novos do que duas semanas de idade e, claro, os commits que estão acessíveis a partir disso; então, fornecido o branch (ou tag) eliminado que possui commits recentes, ele permanecerá em torno do repositório git por até duas quinzenas.

Evitando problemas futuros

Ambos branches e tags podem ser apagados, e ao invocar uma operação push remota de um branch ausente (ou tag) no lado do cliente pode invocar um delete; por exemplo: git push github :refs/heads/master vai apagar o branch ‘master’ fora do repositório remoto conhecido como github. Se isso for em um script, como git push github $COMIT:refs/heads/master e a variável está incorreta (portanto, avaliada como a string vazia), isso pode inadvertidamente excluir o branch. (O mesmo é verdadeiro para as tags em: :refs/tags/.)

Um repositório remoto pode desativar essas operações com a configuração receive.denyDeletes para impedir qualquer ref deletion, e evitar branches não-fast-foward com o receive.denyNonFastforwards. Se qualquer um desses for estabelecido, então as exclusões não terão nenhuma operação e pushes não podem substituir o código que não segue estritamente o histórico. (Isso é ocasionalmente uma operação útil; é provável que seja necessário proporcionar um meio para elevá-la em certas situações.)

Além disso, assegurar que os branches têm core.logAllRefUpdates irá garantir que o repositório ainda mantém o histórico dos branches, pelo menos para os dias de gc.reflogexpire e de gc.reflogexpireunreachable.

Resumo

Enquanto o git pode ser usado, há opções poderosas que podem ajustar ou restringir o seu comportamento. Diante de scripts que têm pleno acesso ao repositório remoto, é aconselhável ter um conjunto mais controlado de opções em vez do padrão “você-pode-fazer-qualquer-abordagem”. Com esse conhecimento em mente, você poderá definir as suas opções de forma adequada para seu ambiente.

***

Texto original disponível em http://alblue.bandlem.com/2011/11/git-tip-of-week-gc-and-pruning-this.html