Remover ramificações de rastreamento que não estão mais no controle remoto


1173

Existe uma maneira simples de excluir todas as ramificações de rastreamento cujo equivalente remoto não existe mais?

Exemplo:

Ramos (local e remoto)

  • mestre
  • origem / mestre
  • origem / bug-fix-a
  • origem / bug-fix-b
  • origem / bug-fix-c

Localmente, eu só tenho uma filial principal. Agora eu preciso trabalhar no bug-fix-a , para que eu faça check-out, trabalhe nele e transfira as alterações para o controle remoto. Em seguida, faço o mesmo com o bug-fix-b .

Ramos (local e remoto)

  • mestre
  • bug-fix-a
  • bug-fix-b
  • origem / mestre
  • origem / bug-fix-a
  • origem / bug-fix-b
  • origem / bug-fix-c

Agora eu tenho ramificações locais master , bug-fix-a , bug-fix-b . O mantenedor da ramificação mestre mesclará minhas alterações no mestre e excluirá todas as ramificações que ele já mesclou.

Portanto, o estado atual é agora:

Ramos (local e remoto)

  • mestre
  • bug-fix-a
  • bug-fix-b
  • origem / mestre
  • origem / bug-fix-c

Agora eu gostaria de chamar algum comando para excluir as ramificações (neste caso, bug-fix-a , bug-fix-b ), que não são mais representadas no repositório remoto.

Seria algo como o comando existente git remote prune origin, mas mais parecido git local prune origin.

Respostas:


1282

git remote prune origin ameixas secas que rastreiam ramos que não estão no controle remoto.

git branch --merged lista as ramificações que foram mescladas na ramificação atual.

xargs git branch -d exclui ramificações listadas na entrada padrão.

Tenha cuidado ao excluir os ramos listados por git branch --merged. A lista pode incluir masterou outros ramos que você prefere não excluir.

Para ter a oportunidade de editar a lista antes de excluir ramificações, faça o seguinte em uma linha:

git branch --merged >/tmp/merged-branches && \
  vi /tmp/merged-branches && xargs git branch -d </tmp/merged-branches

17
A primeira linha das ramificações mescladas está * masterno meu sistema. O seguinte comando funcionou para mim:git branch -d $(git branch --merged |tail -n +2)
Trendfischer

99
Se eu estiver, developentão git branch --mergedinclui master! Você provavelmente (definitivamente!) Não quer excluir isso. Também acho que deveria ser git branch -donde minúsculas -dsignifica "exclusão segura", por exemplo, somente exclusão se mesclada.
thom_nic

11
Parece que uma solução aprimorada é fornecida .
Sergey Brunov 24/03

34
Removido mesclado é útil, mas não o mesmo que "remover ramificações que não estão no controle remoto".
dlsso

11
Basta usar grep para excluir master:git branch --merged | grep -v "master" >/tmp/merged-branches && vi /tmp/merged-branches && xargs git branch -d </tmp/merged-branches
geniass 27/02

592

Após o comando

git fetch -p

remove as referências remotas quando você executa

git branch -vv

mostrará 'ido' como o status remoto. Por exemplo,

$ git branch -vv
  master                 b900de9 [origin/master: behind 4] Fixed bug
  release/v3.8           fdd2f4e [origin/release/v3.8: behind 2] Fixed bug
  release/v3.9           0d680d0 [origin/release/v3.9: behind 2] Updated comments
  bug/1234               57379e4 [origin/bug/1234: gone] Fixed bug

Portanto, você pode escrever um script simples para remover ramificações locais remotas:

git fetch -p && for branch in $(git branch -vv | grep ': gone]' | awk '{print $1}'); do git branch -D $branch; done

Observe que o acima usa o comando "porcelain" git branchpara obter o status upstream.

Outra maneira de obter esse status é usar o comando "encanamento" git for-each-refcom a variável de interpolação %(upstream:track), que será [gone]exatamente como acima.

Essa abordagem é um pouco mais segura, porque não há risco de correspondência acidental em parte da mensagem de confirmação.

git fetch -p && for branch in $(git for-each-ref --format '%(refname) %(upstream:track)' refs/heads | awk '$2 == "[gone]" {sub("refs/heads/", "", $1); print $1}'); do git branch -D $branch; done

8
@KrzysztofWende - não no Solaris e alguns BSDs e alguns OS X :)
JWW

4
Parece que isso também removerá qualquer ramificação que "foi" na última mensagem de confirmação.
dlsso

11
@ dlsso Se a última mensagem de confirmação contiver a string ": gone]", sim será removida também. Você pode torná-lo mais robusto à custa da simplicidade, com um awk / gawk adicional para remover a mensagem de confirmação. git branch -vv | gawk '{print $1,$4}' | grep 'gone]' | gawk '{print $1}'
Jason.rickman

2
Em resposta ao meu comentário anterior (pergunta), o ramo atual possui * como o primeiro campo. Se por acaso também estiver na lista de ramificações "desaparecidas", $ 1 será atribuído * e será interpretado como um tipo de arquivo com o awk exibindo nomes de arquivos e pastas. I eliminou a expressão grep e awk tinha fazer todo o filtragem: awk '/: gone]/{if ($1!="*") print $1}'. Isso agora funciona como esperado.
Rhaben

5
@ahmedbhs, já que o comando usa aspas simples ', você precisa usar aspas duplas "para envolver todo o comando. Além disso, os aliases git que são comandos do shell (como este) exigem! no início. Isso funciona para mim:test = "!git fetch -p && for branch in `git branch -vv | grep ': gone]' | awk '{print $1}'`; do git branch -D $branch; done"
jason.rickman

306

A maioria dessas respostas não responde à pergunta original. Fiz várias escavações e essa foi a solução mais limpa que encontrei. Aqui está uma versão um pouco mais completa dessa resposta:

  1. Confira seu ramo padrão. Usualmentegit checkout master
  2. Corre git fetch -p && git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -d

Explicação:

Funciona removendo seus desvios de rastreamento e excluindo os locais que mostram que eles "desapareceram" git branch -vv.

Notas:

Se o seu idioma estiver definido como algo diferente do inglês, você precisará mudar gonepara a palavra apropriada. Ramos que são apenas locais não serão tocados. As ramificações que foram excluídas no controle remoto, mas não foram mescladas, mostrarão uma notificação, mas não serão excluídas no local. Se você deseja excluí-los, mude -dpara -D.


2
para o sistema operacional francês que se foi precisa ser alterado por díspares
Mohamed EL HABIB

4
deve adicionar LANG = en_US antes da ramificação do git para forçar o inglês: git fetch --prune && LANG = en_US ramificação do git -vv | awk '/: ido] / {print $ 1}' | xargs git branch -d
Mohamed EL HABIB

3
Eu adicionaria git checkout master && ...no início do comando.
Iarroyo 02/11

2
Isso é perigoso quando você está em um ramo que deveria ser excluído - nesse caso, a primeira coluna é '*', que é passada para xargs. Para melhorar isso, adicione o caractere '*' antes de passar a saída para o awk: sed -e 's / ^ * //'
meeee

6
Cuidado! Existe um caso extremo que resultará na exclusão de todas as suas ramificações locais: se você estiver atualmente em uma ramificação que foi excluída no controle remoto, a saída git branch -vvcomeçará com um asterisco que acabará resultando na execução git branch -d *. Aqui está uma versão corrigida que irá ignorar linhas com asterisco:git branch -vv | grep ': gone]'| grep -v "\*" | awk '{ print $1; }' | xargs -r git branch -d
KCM

210

Normalmente, eu não responderia a uma pergunta que já tem 16 respostas, mas todas as outras respostas estão erradas e a resposta certa é muito simples. A pergunta diz: "Existe uma maneira simples de excluir todas as ramificações de rastreamento cujo equivalente remoto não existe mais?"

Se "simples" significa excluir todos eles de uma só vez, não frágeis, não perigosos e sem depender de ferramentas que nem todos os leitores terão, a resposta certa é: não.

Algumas respostas são simples, mas elas não fazem o que foi solicitado. Outros fazem o que foi solicitado, mas não são simples: todos contam com a análise da saída do Git por meio de comandos de manipulação de texto ou linguagens de script, que podem não estar presentes em todos os sistemas. Além disso, a maioria das sugestões usa comandos de porcelana, cuja saída não é projetada para ser analisada por script ("porcelana" refere-se aos comandos destinados à operação humana; os scripts devem usar os comandos "encanamento" de nível inferior).

Leitura adicional:


Se você quiser fazer isso com segurança, no caso de uso em questão (ramificações de rastreamento de coleta de lixo que foram excluídas no servidor, mas ainda existem como ramificações locais) e apenas com comandos de alto nível Git, é necessário

  • git fetch --prune(ou git fetch -p, que é um alias, ou git prune remote originque faz a mesma coisa sem buscar e provavelmente não é o que você deseja na maioria das vezes).
  • Observe todas as ramificações remotas relatadas como excluídas. Ou, para encontrá-los mais tarde, git branch -v(qualquer ramo de rastreamento órfão será marcado como "[foi]").
  • git branch -d [branch_name] em cada ramo de rastreamento órfão

(que é o que algumas das outras respostas propõem).

Se você deseja criar um script para uma solução, então for-each-refé o seu ponto de partida, como na resposta de Mark Longair aqui e nesta resposta a outra pergunta , mas não consigo encontrar uma maneira de explorá-la sem escrever um loop de script de shell ou usar xargs ou algo assim .


Explicação em segundo plano

Para entender o que está acontecendo, você precisa entender que, na situação de rastrear ramificações, você não tem uma ramificação, mas três. (E lembre-se de que "ramificação" significa simplesmente um ponteiro para uma confirmação.)

Dada uma ramificação de rastreamento feature/X, o repositório remoto (servidor) terá essa ramificação e a chamará feature/X. Seu repositório local possui uma ramificação remotes/origin/feature/Xque significa "Isso foi o que o controle remoto me disse sobre sua ramificação de recursos / X, da última vez que conversamos" e, finalmente, o repositório local possui uma ramificação feature/Xque aponta para sua confirmação mais recente e está configurada para "track" remotes/origin/feature/X, o que significa que você pode puxar e empurrar para mantê-los alinhados.

Em algum momento, alguém excluiu feature/Xo controle remoto. A partir desse momento, você fica com o seu local feature/X(o que provavelmente não quer mais, pois o trabalho no recurso X provavelmente está concluído) e o seu remotes/origin/feature/Xcom certeza é inútil, pois seu único objetivo era lembrar o estado da ramificação do servidor .

E o Git permitirá que você limpe automaticamente os redundantes remotes/origin/feature/X- é o que git fetch --prunefaz - mas, por algum motivo, ele não exclui automaticamente os seus feature/X... mesmo que você feature/Xainda contenha as informações de rastreamento órfãs, ele tem as informações para identificar ramificações de rastreamento anteriores que foram totalmente mescladas. (Afinal, ele pode dar -lhe a informação que lhe permite fazer a operação com a mão mesmo.)


3
Esta é uma resposta muito mais segura. Além disso, funcionará se você usar um fluxo de trabalho "Squash and Merge", diferente da resposta selecionada.
Jake Levitt

3
Isso realmente responde apenas à parte fácil da pergunta "como encontrar desvios" (e com o mesmo comando que já foi publicado por jason.rickman em 2015), mas depois diz para você excluir todos os desvios manualmente, exatamente o que o OP não quer fazer.
Voo

4
@Voo Esse é o ponto da resposta: não dizer como fazê-lo (isso é apenas um bônus), mas dizer que a resposta é que não existe uma maneira fácil e simples de fazê-lo. Qual é a resposta correta para a pergunta que foi formulada.
Andrew Spencer

1
"Se você quiser fazer isso com segurança, para o caso de uso na pergunta .." - que nega a maioria das reivindicações do primeiro parágrafo. "[Git] pode fornecer as informações que permitem que você faça a operação manualmente" - somos programadores, portanto, considerando as informações como uma lista (que é mostrada), a tarefa ainda é simples (por exemplo xargs). Também há git aliaspara simplificar vários casos.
user2864740

2
@ user2864740 Alguns leitores podem considerar que escrever um pequeno script bash se enquadra na definição de "simples", e essa é uma posição perfeitamente defensável, mas dei minha própria interpretação de "simples" no segundo parágrafo e o restante da resposta é consistente com isso. E em defesa da minha interpretação reconhecidamente restritiva, nem todos os usuários do Git têm o xargsbash, e provavelmente não estão aqui procurando um desafio de microcodificação, tanto quanto uma resposta rápida que podem ser aplicados com segurança sem se distrair do objetivo real .
Andrew Spencer

52

Encontrei a resposta aqui: Como posso excluir todos os ramos git que foram mesclados?

git branch --merged | grep -v "\*" | xargs -n 1 git branch -d

Certifique-se de manter o mestre

Você pode garantir que master, ou qualquer outra ramificação, não seja removida adicionando outra grepapós a primeira. Nesse caso, você iria:

git branch --merged | grep -v "\*" | grep -v "YOUR_BRANCH_TO_KEEP" | xargs -n 1 git branch -d

Então, se quiséssemos manter master, develope stagingpor exemplo, iríamos:

git branch --merged | grep -v "\*" | grep -v "master" | grep -v "develop" | grep -v "staging" | xargs -n 1 git branch -d

Tornar este um alias

Como é um pouco longo, convém adicionar um alias ao seu .zshrcou .bashrc. O meu é chamado gbpurge(para git branches purge):

alias gbpurge='git branch --merged | grep -v "\*" | grep -v "master" | grep -v "develop" | grep -v "staging" | xargs -n 1 git branch -d'

Em seguida, recarregue seu .bashrcou .zshrc:

. ~/.bashrc

ou

. ~/.zshrc

8
Útil, mas não é o mesmo como "ramos remover não na remota"
dlsso

Greate resposta @karlingen, eu tenho essa permanentemente como gbpurge em todos os ambientes meus dev
Peter Dolan

50

Solução Windows

Para o Microsoft Windows Powershell:

git checkout master; git remote update origin --prune; git branch -vv | Select-String -Pattern ": gone]" | % { $_.toString().Trim().Split(" ")[0]} | % {git branch -d $_}

Explicação

git checkout master muda para o ramo principal

git remote update origin --prune ameixas ramos remotos

git branch -vvobtém uma saída detalhada de todos os ramos ( referência do git )

Select-String -Pattern ": gone]" obtém apenas os registros de onde foram removidos do controle remoto.

% { $_.toString().Trim().Split(" ")[0]} obter o nome do ramo

% {git branch -d $_} exclui a ramificação


3
Obrigado por isso, embora eu tenha que adicionar um .Trim()depois .toString()para remover dois espaços antes do nome do ramo.
Matthew Matthew

2
Obrigado por este comando. Eu tive que mudar git branch -dpara para git branch -Dmais eu recebo o erro que o ramo não está totalmente mesclado.
markieo

1
@markieo Você provavelmente já sabe disso, mas para qualquer outra pessoa - git branch -Dé destrutivo e excluirá o trabalho local que você ainda não enviou. -dé sempre seguro, apenas tenha cuidado -D.
Nate Barbettini

33

A correspondência de padrões para "ido" na maioria das outras soluções foi um pouco assustadora para mim. Para ser mais seguro, isso usa o --formatsinalizador para retirar o status de rastreamento upstream de cada filial .

Eu precisava de uma versão compatível com o Windows, portanto, isso exclui todos os ramos listados como "desaparecidos" usando o Powershell:

git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" | 
    ? { $_ -ne "" } | 
    % { git branch -D $_ }

A primeira linha lista o nome das ramificações locais cuja ramificação upstream está "ausente". A próxima linha remove as linhas em branco (que são enviadas para as ramificações que não desapareceram) e o nome da ramificação é passado ao comando para excluir a ramificação.


6
A --formatopção parece ser bastante nova; Eu precisava atualizar o git de 2.10.something para 2.16.3 para obtê-lo. Esta é a minha modificação para sistemas Linux-ish:git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname)%(end)" | sed 's,^refs/heads/,,' | grep . | xargs git branch -D
bxm 27/03

2
Esta é a melhor solução até agora. Uma sugestão é usar refname:short. Então você pode excluir a linha% { $_ -replace '^refs/heads/', '' }
ioudas

2
Esta é uma ótima solução para Windows. Estou usando-o dessa forma, pois é mais legível. git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" | where { $_ -ne "" } | foreach { git branch -d $_ }Provavelmente também é uma boa idéia usar em -dvez de -D. A exclusão forçada não deve ser necessária para ramificações que não estão mais no controle remoto.
Rubanov 04/09/19

31

Remova todas as ramificações que foram mescladas no mestre, mas não tente remover o próprio mestre:

git checkout master && git pull origin master && git fetch -p && git branch -d $(git branch --merged | grep master -v)

ou adicione um alias:

alias gitcleanlocal="git checkout master && git pull origin master && git fetch -p && git branch -d $(git branch --merged | grep master -v)"

Explicação:

git checkout master ramo mestre de checkout

git pull origin master garantir que a filial local tenha todas as alterações remotas mescladas

git fetch -p remova referências a ramificações remotas que foram excluídas

git branch -d $(git branch master --merged | grep master -v) exclua todas as ramificações que foram mescladas no mestre, mas não tente remover o próprio mestre


3
Uma observação: isso é muito útil, mas também removerá os ramos que nunca foram enviados ao controle remoto. É mais seguro únicas diferenças de lista e, em seguida, copiar o que você realmente quer apagar em git branch -Dcomando
Zefiryn

Apenas para esclarecer, Zefiryn está se referindo ao uso da opção -D, que não faz parte da linha única.
CS01

1
Ou use a letra minúscula, git branch -dque deve emitir um aviso sobre ramos não enviados .
Acme

16
git fetch -p

Isso removerá todos os ramos que não existem mais no controle remoto.


64
isso remove referências remotas, mas não as próprias ramificações locais. Embora seja um comando útil, acho que isso não responde à pergunta do OP.
thataustin

O que? Isso exclui filiais locais para mim.
Alex Hall

1
@AlexHall O repositório remoto tem uma ramificação X; você git checkout X; agora seu repositório possui uma ramificação de rastreamento (local) Xe uma ramificação remota origin/X; o repositório remoto exclui X; você git fetch-p; no seu repositório local, não apenas origin/Xmas também Xforam excluídos. É isso que você está dizendo?
Andrew Spencer

16

Mais uma resposta para a pilha, baseando-se fortemente na resposta de Patrick (que eu gosto porque parece acabar com qualquer ambiguidade sobre onde gone]será a correspondência na git branchsaída), mas adicionando uma * nix dobrada.

Na sua forma mais simples:

git branch --list --format \
  "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)" \
  | xargs git branch -D

Eu tenho isso embrulhado em um git-gonescript no meu caminho:

#!/usr/bin/env bash

action() {
  ${DELETE} && xargs git branch -D || cat
}

get_gone() {
  git branch --list --format \
    "%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)"
}

main() {
  DELETE=false
  while [ $# -gt 0 ] ; do
    case "${1}" in
      (-[dD] | --delete) DELETE=true ;;
    esac
    shift
  done
  get_gone | action
}

main "${@}"

NB - A --formatopção parece ser relativamente nova; Eu precisava atualizar o git de 2.10.something para 2.16.3 para obtê-lo.

EDIT: tweaked para incluir sugestão sobre refname:shortde Benjamin W.

NB2 - Eu só testei bash, daí o hashbang, mas provavelmente portátil sh.


Você não pode pular a sedparte usando %(refname:short)na primeira linha?
Benjamin W.

Parece funcionar para mim, e eu o tenho como um alias puro do Git agora - a solução mais limpa de todos eles aqui!
Benjamin W.

2.13 introduzido --format. Outra maneira de pular a sedpeça é usar git update-ref -d. Observe que isso provavelmente é um tanto perigoso, o uso git for-each-refé mais seguro aqui (fornecido --shell).
gsnedders 23/05/19

Ótima resposta, e isso me ajudou a responder minhas próprias perguntas ao me debater com --formatisso. Apenas uma pergunta: por que o #!bash? Tudo aqui parece portátil shpara mim.
Toby Speight

Esse é apenas o meu boilerplate normal, e não havia testado em outro lugar.
bxm 6/03

15

Pode ser útil para alguns, uma linha simples para limpar todos os ramos locais, exceto o mestre e o desenvolvimento

git branch | grep -v "master" | grep -v "develop" | xargs git branch -D

Resposta incrível! Eu gosto de como alguém pode facilmente brincar com esse 'git branch | grep -v "master" | grep -v "develop"tipo de coisa antes de se comprometer a adicionar a parte de exclusão do comando. Fin
finneycanhelp 8/03/19

Mas isso não responde à pergunta acima. Isso excluirá as ramificações, embora o controle remoto ainda esteja lá.
Jim.B 18/03

13

Isso excluirá todo o ramificado local mesclado, exceto a referência mestre local e o que está sendo usado atualmente:

git branch --merged | grep -v "*" | grep -v "master" | xargs git branch -d

E isso excluirá todos os ramos que já foram removidos do repositório remoto referenciado por " origem ", mas ainda estão disponíveis localmente em " controles remotos / origem ".

git remote prune origin

git branch -vv | grep 'gone]' | grep -v "\*" | awk '{print $1}' | xargs -r git branch -d Explicação: Prefiro substituir git branch --mergedby por git branch -vvpara mostrar o status (desaparecido) porque o anterior git branch --mergedtambém pode mostrar o mestre
jpmottin

13

Eu não acho que exista um comando interno para fazer isso, mas é seguro fazer o seguinte:

git checkout master
git branch -d bug-fix-a

Quando você usa -d, o git se recusa a excluir a ramificação, a menos que ela seja completamente mesclada HEADou sua ramificação de rastreamento remoto upstream. Portanto, você sempre pode fazer um loop sobre a saída git for-each-refe tentar excluir cada ramificação. O problema com essa abordagem é que eu suspeito que você provavelmente não deseja bug-fix-dser excluído apenas porque origin/bug-fix-dcontém seu histórico. Em vez disso, você pode criar um script parecido com o seguinte:

#!/bin/sh

git checkout master &&
for r in $(git for-each-ref refs/heads --format='%(refname:short)')
do
  if [ x$(git merge-base master "$r") = x$(git rev-parse --verify "$r") ]
  then
    if [ "$r" != "master" ]
    then
      git branch -d "$r"
    fi
  fi
done

Aviso: não testei esse script - use apenas com cuidado ...


Tomei a liberdade de editar o script. Ainda não dará nenhuma garantia, mas funciona e parece funcionar agora.
Fwielstra

13

TL; DR:

Remova TODAS as ramificações locais que não estão no controle remoto

git fetch -p && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs git branch -D

Remova TODAS as ramificações locais que não estão no remoto E que estão totalmente mescladas E que não são usadas como dito em muitas respostas anteriores.

git fetch -p && git branch --merged | grep -v '*' | grep -v 'master' | xargs git branch -d

Explicação

  • git fetch -p podará todas as ramificações que não existem mais no controle remoto
  • git branch -vv imprimirá galhos locais e galhos podados serão marcados com gone
  • grep ': gone]' seleciona apenas o ramo que se foi
  • awk '{print $1}' filtre a saída para exibir apenas o nome das ramificações
  • xargs git branch -D irá percorrer todas as linhas (ramificações) e forçar a remover essa ramificação

Por que git branch -De não git branch -dmais você terá para ramos que não são totalmente mesclados.

error: The branch 'xxx' is not fully merged.

1
você quer dizer remoto em vez de mestre?
tinos

8

Você poderia fazer isso:

git branch -vv | grep 'origin/.*: gone]' | awk '{print $1}' | xargs git branch -d

7

Com base nas informações acima, isso funcionou para mim:

git br -d `git br -vv | grep ': gone] ' | awk '{print $1}' | xargs`

Ele remove todas as ramificações locais com estão ': gone] 'no controle remoto.


1
Parece que isso também removerá qualquer ramificação que "foi" na última mensagem de confirmação.
dlsso

Ele também removerá qualquer ramificação que tenha gonequalquer lugar em seu nome.
precisa saber é o seguinte

3
"git branch -D git branch -vv | grep ': ido]' | awk '{print $ 1}' | xargs`" Isso fez o trabalho para mim.
Muthu Ganapathy Nathan

7
grep gone <(git branch -v) | cut -d ' ' -f 3 | xargs git branch -d

O comando acima pode ser usado para buscar ramificações que são mescladas e excluídas no controle remoto e exclui o ramo local que não está mais disponível no controle remoto


A melhor solução até agora, embora eu a tenha alterado grep gone <(git branch -v) | cut -d ' ' -f 3 | xargs git branch -Dpara forçar a excluir tudo #
Shoaib

Perigoso se algum dos nomes de sua filial contiver a substring goneem qualquer lugar (por exemplo usingonefunction).
Toby Speight

7

Nada disso foi realmente certo para mim. Eu queria algo que limpasse todas as ramificações locais que estavam rastreando uma ramificação remota origin, onde o ramo remoto foi excluído ( gone). Eu não queria excluir ramificações locais que nunca foram configuradas para rastrear uma ramificação remota (ou seja: minhas ramificações locais de desenvolvimento). Também queria um one-liner simples que apenas usasse git, ou outras ferramentas CLI simples, em vez de escrever scripts personalizados. Acabei usando um pouco de grepe awkpara fazer esse comando simples.

Isso foi o que acabou no meu ~/.gitconfig:

[alias]
  prune-branches = !git remote prune origin && git branch -vv | grep ': gone]' | awk '{print $1}' | xargs -r git branch -D

Aqui está um git config --global ...comando para adicionar isso facilmente como git prune-branches:

git config --global alias.prune-branches '!git remote prune origin && git branch -vv | grep '"'"': gone]'"'"' | awk '"'"'{print $1}'"'"' | xargs -r git branch -d'

NOTA: No comando config, eu uso a -dopção em git branchvez de -D, como na minha configuração real. Uso -Dporque não quero ouvir o Git reclamar de galhos imersos. Você também pode querer essa funcionalidade. Nesse caso, basta usar em -Dvez de -dno final desse comando de configuração.


Eu gosto da abordagem alias do git. Pergunta rápida: Por que fazer git branch -vve saudar : gone]e não fazer git branch -ve saudar [gone]?
Lalibi 14/01/19

@alibi, boa pergunta. Não me lembro. Eu suspeito que algo não estava aparecendo com apenas um v. Se git branch -v | grep '[gone]'funcionar para você, vá em frente. Parece um pouco mais limpo.
Karl Wilbur

4

Baseado na dica do Git: Excluindo ramificações locais antigas , que se parece com a solução de jason.rickman, implementei um comando personalizado para esse propósito, chamado git, usando o Bash:

$ git gone
usage: git gone [-pndD] [<branch>=origin]
OPTIONS
  -p  prune remote branch
  -n  dry run: list the gone branches
  -d  delete the gone branches
  -D  delete the gone branches forcefully

EXAMPLES
git gone -pn    prune and dry run
git gone -d     delete the gone branches

git gone -pn combina a poda e lista os ramos "desaparecidos":

$ git gone -pn
  bport/fix-server-broadcast         b472d5d2b [origin/bport/fix-server-broadcast: gone] Bump modules
  fport/rangepos                     45c857d15 [origin/fport/rangepos: gone] Bump modules

Então você pode puxar o gatilho usando git gone -dou git gone -D.

Notas

  • A expressão regular que usei é "$BRANCH/.*: gone]"onde $BRANCHnormalmente estaria origin. Provavelmente isso não funcionará se a saída do Git estiver localizada em francês etc.
  • Sebastian Wiesner também o portou para o Rust for Windows users. Esse também é chamado de git gone .

Essa provavelmente deve ser a resposta aceita - git goneparece um apelido para git branch -vv | grep 'origin/.*: gone]' | awk '{print $1}' | xargs git branch -d, que resolve o problema do OP.
alex

4

Baseando-se fortemente a partir de um número de outras respostas aqui, eu acabei com o seguinte (git 2,13 e acima só, creio), que deve funcionar em qualquer UNIX-like shell:

git for-each-ref --shell --format='ref=%(if:equals=[gone])%(upstream:track)%(then)%(refname)%(end)' refs/heads | while read entry; do eval "$entry"; [ ! -z "$ref" ] && git update-ref -d "$ref" && echo "deleted $ref"; done

Isso notavelmente usa em for-each-refvez de branch(como branché um comando "porcelana" projetado para saída legível por humanos, não para processamento de máquina) e usa seu --shellargumento para obter uma saída de escape adequada (isso nos permite não nos preocupar com nenhum caractere no nome ref).


Funciona para mim, mas também seria bom se você pudesse explicar o que as outras etapas do comando estão fazendo. Por exemplo, não sei o que [ ! -z "$ref" ]significa. Eu acho que um multi-liner já teria ajudado. Mas ainda obrigado pela sua contribuição!
KevinH 27/08/19

4

Outra resposta, porque nenhuma das soluções atende às minhas necessidades de elegância e plataforma cruzada:

Comando para excluir filiais locais que não estão no controle remoto:

for b in $(git for-each-ref --format='%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)' refs/heads); do git branch -d $b; done

Para integrá-lo ao gitconfig para que ele possa ser executado com git branch-prune:

Bater

git config --global alias.branch-prune '!git fetch -p && for b in $(git for-each-ref --format='\''%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)'\'' refs/heads); do git branch -d $b; done'

PowerShell

git config --global alias.branch-prune '!git fetch -p && for b in $(git for-each-ref --format=''%(if:equals=[gone])%(upstream:track)%(then)%(refname:short)%(end)'' refs/heads); do git branch -d $b; done'

(Precisa de ajuda para encontrar um comando universal para o PowerShell e o bash)

Por que essa resposta é a melhor?

  • Oferece uma solução completa: adiciona uma git branch-prune comando ao seu git
  • Funciona bem no Windows PowerShell
  • A idéia central é o método à prova de balas de @ jason.rickman usandogit for-each-ref
  • A análise e a filtragem são feitas --filtersem que sejam necessárias dependências externas

Explicação:

  • Adiciona um novo alias ao seu ~\.gitconfig. Depois de executar isso, você pode simplesmente fazergit branch-prune
  • Dentro deste alias:
    • Busca ramificações com a --prunebandeira, que "remove as ramificações de rastreamento remoto que não estão mais no controle remoto"
    • Usa git for-each-refe --filter, para obter uma lista dos ramos são [gone](sem controle remoto)
    • Percorre esta lista e exclui a ramificação com segurança

1
Obrigado, @ jerry-wu, por melhorar a grandiosidade desta solução até o infinito.
Himura 20/04

@ jerry-wu e sua última edição do comando git config não funciona no PowerShell ((
Himura

1

Eu vim com esse script bash. É sempre manter os ramos develop, qa, master.

git-clear() {
  git pull -a > /dev/null

  local branches=$(git branch --merged | grep -v 'develop' | grep -v 'master' | grep -v 'qa' | sed 's/^\s*//')
  branches=(${branches//;/ })

  if [ -z $branches ]; then
    echo 'No branches to delete...'
    return;
  fi

  echo $branches

  echo 'Do you want to delete these merged branches? (y/n)'
  read yn
  case $yn in
      [^Yy]* ) return;;
  esac

  echo 'Deleting...'

  git remote prune origin
  echo $branches | xargs git branch -d
  git branch -vv
}

1

Eu uso um método curto para fazer o truque, eu recomendo que você faça o mesmo, pois isso pode economizar algumas horas e dar mais visibilidade

Basta adicionar o seguinte trecho ao seu .bashrc (.bashprofile no macos).

git-cleaner() { git fetch --all --prune && git branch --merged | grep -v -E "\bmaster|preprod|dmz\b" | xargs -n 1 git branch -d ;};
  1. Buscar todos os controles remotos
  2. Obter apenas ramificações mescladas do git
  3. Remova desta lista os ramos "protegidos / importantes"
  4. Remova o restante (por exemplo, galhos limpos e mesclados)

Você precisará editar a regex grep para atender às suas necessidades (aqui, isso impede a exclusão de master, preprod e dmz)


git fetch --all --prunefez o truque. Obrigado!
Leão - Han Li

1

Isso funcionou para mim:

git branch -r | awk '{print $1}' | egrep -v -f /dev/fd/0 <(git branch -vv | grep origin) | awk '{print $1}' | xargs git branch -d

ah, eu publiquei isso duas vezes em ... só vi isso ao revisar postagens. Mas você realmente pode apontar para o cara que forneceu esse comando, em vez de tomá-lo como seu!
Dwza

Eu obtê-lo a partir de um fórum não tentar ser inteligente basta ter a resposta ou ignorá-lo
Fareed Alnamrouti

Mesmo que você pudesse ... de qualquer maneira ... não veio aqui para discutir isso com você. Como você sayed .. .Pegue meu comentário ou ignorá-lo;)
Dwza

0

Não sei por quanto tempo, mas agora uso o git-up, que cuida disso.

eu faço git up e ele começa a rastrear novas ramificações e excluir as antigas.

Apenas para deixar claro, não é um comando git pronto para uso - https://github.com/aanand/git-up

BTW também esconde árvore suja e faz rebotes ainda com apenas git up.

Espero que seja útil para alguém


2
Isso não está excluindo ramificações locais que não existem no servidor.
Ashley

0

Aqui está uma solução que eu uso para a casca do peixe. Testado em Mac OS X 10.11.5, fish 2.3.0e git 2.8.3.

function git_clean_branches
  set base_branch develop

  # work from our base branch
  git checkout $base_branch

  # remove local tracking branches where the remote branch is gone
  git fetch -p

  # find all local branches that have been merged into the base branch
  # and delete any without a corresponding remote branch
  set local
  for f in (git branch --merged $base_branch | grep -v "\(master\|$base_branch\|\*\)" | awk '/\s*\w*\s*/ {print $1}')
    set local $local $f
  end

  set remote
  for f in (git branch -r | xargs basename)
    set remote $remote $f
  end

  for f in $local
    echo $remote | grep --quiet "\s$f\s"
    if [ $status -gt 0 ]
      git branch -d $f
    end
  end
end

Algumas notas:

Certifique-se de definir o correto base_branch. Nesse caso, eu uso developcomo o ramo base, mas pode ser qualquer coisa.

Esta parte é muito importante: grep -v "\(master\|$base_branch\|\*\)" . Ele garante que você não exclua o mestre ou sua filial base.

Uso git branch -d <branch>como precaução extra, para não excluir nenhum ramo que não tenha sido totalmente mesclado ao HEAD upstream ou atual.

Uma maneira fácil de testar é substituir git branch -d $fpor echo "will delete $f".

Suponho que devo acrescentar: USE POR SEU PRÓPRIO RISCO!


0

Eu escrevi um script Python usando o GitPython para excluir ramificações locais que não existem no controle remoto.

    import git
    import subprocess
    from git.exc import GitCommandError
    import os

    def delete_merged_branches():
        current_dir = input("Enter repository directory:")
        repo = git.Repo(current_dir)
        git_command = git.Git(current_dir)

        # fetch the remote with prune, this will delete the remote references in local.
        for remote in repo.remotes:
            remote.fetch(prune=True)

        local_branches = [branch.name for branch in repo.branches]
        deleted_branches = []

        # deleted_branches are the branches which are deleted on remote but exists on local.
        for branch in local_branches:
            try:
                remote_name = 'origin/'+ branch
                repo.git.checkout(remote_name)
            except GitCommandError:
            # if the remote reference is not present, it means the branch is deleted on remote.
                deleted_branches.append(branch)

        for branch in deleted_branches:
            print("Deleting branch:"+branch)
            git_command.execute(["git", "branch", "-D",branch])


        # clean up the work flow.
        repo.git.checkout('master')
        repo.git.pull()

    if __name__ == '__main__':
        delete_merged_branches()

Espero que alguém ache útil, por favor, adicione comentários se eu perdi alguma coisa.


0

Se você estiver usando o zshshell com o Oh My Zshinstalado, a maneira mais fácil de fazer isso com segurança é usar o preenchimento automático embutido.

Primeiro, determine com quais ramificações você deseja excluir:

~ git branch --merged

  branch1
  branch2
  branch3
* master

isso mostrará uma lista de ramos já mesclados

Depois de conhecer alguns que deseja excluir, digite:

~ git branch -d 

Tudo o que você precisa fazer é clicar em [tab] e ele exibirá uma lista dos ramos locais. Use tab-complete ou apenas pressione [tab] novamente e você pode alternar entre eles para selecionar um ramo com [enter].

Guia Selecione os ramos repetidamente até ter uma lista dos ramos que deseja excluir:

~ git branch -d branch1 branch2 branch3

Agora basta pressionar Enter para excluir sua coleção de filiais.

Se você não estiver usando o zsh no seu terminal ... Obtenha aqui.


0

Eu gosto de usar pipes porque facilita a leitura do comando.

Esta é a minha solução se você deseja remover todos os ramos, exceto o mestre.

git branch | grep -v master | xargs -n 1 git branch -D

Para excluir outras ramificações que correspondem aos seus critérios, modifique o primeiro e o segundo bloco.

git branch --merged | grep feature_name | xargs -n 1 git branch -D

-3

Aqui está a resposta simples que funcionou para mim usando um cliente git:

Exclua completamente o repositório da sua máquina e faça o check-out novamente.

Não mexa com scripts arriscados.


2
E quanto aos ramos em que estou trabalhando atualmente: /
Mailo Světel

@ MailoSvětel se eles estão em seu controle remoto, então você pode simplesmente retirá-los novamente (lembre-se de cometer e empurrar o seu mais recente trabalho para o controle remoto!)
Juan Carlos Ospina Gonzalez

Na minha pergunta e na maioria das respostas, estamos tentando evitar trabalhos desnecessários. Em outras palavras: o que eu preciso fazer é remover os galhos, não vou mais trabalhar. Remover o repositório, cloná-lo e começar a rastrear as ramificações novamente, é um trabalho desnecessário para mim. Além disso, algum trabalho pode se perder :(
Mailo Světel
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.