Git prune
O comando git prune
é um utilitário interno de limpeza que elimina objetos Git inacessíveis ou "órfãos". Objetos inalcançáveis são aqueles que são inacessíveis por qualquer ref. Qualquer commit que não puder ser acessado por meio de uma ramificação ou marcador é considerado inacessível. Em geral, o git prune
não é executado de imediato. Prune é considerado um comando de coleta de lixo e é um comando filho do comando git gc.
Visão geral do Git Prune
Para entender os efeitos do git prune
, precisamos simular um cenário em que um commit se torne inacessível. A seguir está uma sequência de execuções de linha de comando que vai simular essa experiência.
~ $ cd git-prune-demo/
~/git-prune-demo $ git init .
Initialized empty Git repository in /Users/kev/Dropbox/git-prune-demo/.git/
~/git-prune-demo $ echo "hello git prune" > hello.txt
~/git-prune-demo $ git add hello.txt
~/git-prune-demo $ git commit -am "added hello.txt"
A sequência anterior de comandos vai criar um novo repositório em um diretório chamado git-prune-demo
. Um commit que consiste em um novo arquivo hello.txt
é adicionado ao repositório com o conteúdo básico de "hello git prune". Em seguida, vamos modificar o hello.txt
e criar um novo commit a partir dessas modificações.
~/git-prune-demo $ echo "this is second line txt" >> hello.txt
~/git-prune-demo $ cat hello.txt
hello git prune
this is second line txt
~/git-prune-demo $ git commit -am "added another line to hello.txt"
[main 5178bec] added another line to hello.txt
1 file changed, 1 insertion(+)
Material relacionado
Como mover um Repositório do Git completo
VER SOLUÇÃO
Aprenda a usar o Git com o Bitbucket Cloud
Agora temos um histórico de 2 commits neste repositório de demonstração. Podemos verificar usando git log
:
~/git-prune-demo $ git log
commit 5178becc2ca965e1728554ce1cb8de2f2c2370b1
Author: kevzettler <kevzettler@gmail.com>
Date: Sun Sep 30 14:49:59 2018 -0700
added another line to hello.txt
commit 994b122045cf4bf0b97139231b4dd52ea2643c7e
Author: kevzettler <kevzettler@gmail.com>
Date: Sun Sep 30 09:43:41 2018 -0700
added hello.txt
A saída do git log exibe os dois commits e as mensagens de commit correspondentes sobre as edições feitas no hello.txt
. O próximo passo é tornar um dos commits inacessível, o que vai ser feito com o comando git reset. A gente redefiniu o estado do repositório para o primeiro commit, o commit "hello.txt" adicionado.
~/git-prune-demo $ git reset --hard 994b122045cf4bf0b97139231b4dd52ea2643c7e
HEAD is now at 994b122 added hello.txt
Se agora a gente examinar o estado do repositório com o git log
, vai ver que tem apenas um commit.
~/git-prune-demo $ git log
commit 994b122045cf4bf0b97139231b4dd52ea2643c7e
Author: kevzettler <kevzettler@gmail.com>
Date: Sun Sep 30 09:43:41 2018 -0700
added hello.txt
O repositório de demonstração agora está em um estado que contém um commit desanexado. O segundo commit que fizemos com a mensagem "outra linha adicionada ao hello.txt" não é mais exibida na saída do git log
e agora está desanexado. Pode parecer que perdemos ou excluímos o commit, mas o Git é muito rigoroso quanto a não excluir o histórico. Podemos confirmar que ele ainda está disponível, mas desanexado, usando o git checkout
para fazer uma visita direta:
~/git-prune-demo $ git checkout 5178becc2ca965e1728554ce1cb8de2f2c2370b1
Note: checking out '5178becc2ca965e1728554ce1cb8de2f2c2370b1'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 5178bec... added another line to hello.txt
~/git-prune-demo $ git log
commit 5178becc2ca965e1728554ce1cb8de2f2c2370b1
Author: kevzettler <kevzettler@gmail.com>
Date: Sun Sep 30 14:49:59 2018 -0700
added another line to hello.txt
commit 994b122045cf4bf0b97139231b4dd52ea2643c7e
Author: kevzettler <kevzettler@gmail.com>
Date: Sun Sep 30 09:43:41 2018 -0700
added hello.txt
Quando consultamos o commit desanexado, o Git é atencioso o suficiente para nos dar uma mensagem detalhada explicando que estamos em um estado desanexado. Se examinarmos o log aqui, podemos ver que o commit "outra linha adicionada ao hello.txt" agora está de volta na saída do log! Agora que sabemos que o repositório está em um bom estado de simulação com um commit desanexado, podemos praticar o uso do git prune
. Primeiro, vamos retornar ao branch main
usando git checkout
.
~/git-prune-demo $ git checkout main
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
5178bec added another line to hello.txt
If you want to keep it by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> 5178bec
Switched to branch 'main'
Ao retornar ao principal via git checkout
, o Git é atencioso o suficiente mais uma vez para nos informar que estamos deixando um commit desanexado para trás. Agora é hora de remover o commit desanexado! Em seguida, vamos executar o git prune
, mas devemos ter certeza de passar algumas opções para ele. --dry-run
e --verbose
vão exibir a saída indicando o que está definido para ser removido, mas não vai remover as entradas de verdade.
~/git-prune-demo $ git prune --dry-run --verbose
É provável que esse comando retorne uma saída vazia. A saída vazia implica que o prune não vai excluir nada. Mas por quê? Bem, o commit provavelmente não está totalmente desanexado. Em algum lugar, o Git ainda mantém uma referência a ele. Este é um excelente exemplo de por que o git prune
não deve ser usado de forma independente fora do git gc
. Esse também é um bom exemplo de como é difícil perder os dados por completo com o Git.
O mais provável é que o Git esteja armazenando uma referência ao nosso commit desanexado no reflog. A gente pode investigar executando git reflog. Você deve ver alguma saída descrevendo a sequência de ações que a gente tomou para chegar aqui. Para mais informações sobre o git reflog
, visite a página git reflog. Além de preservar o histórico no reflog, o Git tem datas de expiração internas de quando vai remover os commits desanexados. Repetindo, essas são todas as informações de implementação que o git gc
manipula e o git prune
não deve ser usado de forma independente.
Para concluir nossa demonstração de simulação do git prune
, precisamos limpar o reflog.
~/git-prune-demo $ git reflog expire --expire=now --expire-unreachable=now --all
O comando acima vai forçar a expiração de todas as entradas para o reflog que são mais antigas do que agora. Este é um comando brutal e perigoso que você nunca deveria ter que usar sendo um usuário casual do Git. Estamos executando esse comando para demonstrar um git prune
bem-sucedido. Com o reflog totalmente apagado, agora podemos executar o git prune
.
~/git-prune-demo $ git prune --dry-run --verbose --expire=now
1782293bdfac16b5408420c5cb0c9a22ddbdd985 blob
5178becc2ca965e1728554ce1cb8de2f2c2370b1 commit
a1b3b83440d2aa956ad6482535cbd121510a3280 commit
f91c3433eae245767b9cd5bdb46cd127ed38df26 tree
Esse comando deve gerar uma lista de referências de objetos Git SHA que se parece com a de cima.
Uso
O git prune
tem uma pequena lista de opções que abordamos na seção de visão geral.
-n --dry-run
Não execute o prune. Apenas mostre uma saída do que ele vai fazer
-v --verbose
Exibe a saída de todos os objetos e ações tomadas pelo prune
--progress
Exibe a saída que indica o progresso do prune
--expire <time>
Forçar a expiração de objetos que já passaram do
<head>…
Especificar um <head>
vai preservar todas as opções dessa ref de cabeçalho
Discussão
Qual é a diferença entre Git Prune, Git Fetch --prune e Git Remote Prune?
git remote prune
e git fetch --prune
fazem a mesma coisa: excluem as referências para branches que não existem no remoto. Essa ação é bastante desejável ao trabalhar em um fluxo de trabalho de equipe no qual os branches remotos são excluídos após o merge para o main
. O segundo comando, git fetch --prune
, vai conectar ao remoto e buscar o estado remoto mais recente antes da remoção. É essencialmente uma combinação de comandos:
git fetch --all && git remote prune
O comando genérico git prune
é totalmente diferente. Conforme discutido na seção de visão geral, o git prune vai excluir os commits desanexados no local.
Como faço para limpar branches desatualizados?
O git fetch --prune
é o melhor utilitário para limpar branches desatualizados. Ele vai se conectar a um repositório remoto compartilhado e vai buscar todas as referências de branches remotos. Em seguida, ele vai excluir as referências remotas que não estão mais em uso no repositório remoto.
O Git Remote Prune Origin exclui o branch local?
Não, o git remote prune origin
vai excluir apenas as refs para branches remotos que não existem mais. O Git armazena as referências locais e remotas. Um repositório vai ter coleções de referência local/origin
e remote/origin
. git remote prune origin
só vai remover as refs em remote/origin
. Assim, o trabalho local fica com segurança em local/origin
.
Resumo do Git Prune
O comando git prune
deve ser invocado como um comando filho para git gc
. É muito improvável que você precise invocar o git prune
no dia a dia da engenharia de software. Outros comandos são necessários para entender os efeitos do git prune
. Alguns comandos usados neste artigo foram git log
, git reflog
e git checkout
.
Compartilhar este artigo
Próximo tópico
Leitura recomendada
Marque esses recursos para aprender sobre os tipos de equipes de DevOps ou para obter atualizações contínuas sobre DevOps na Atlassian.