Liste todas as filiais locais sem um controle remoto

93

Problema: desejo uma maneira de excluir todas as filiais locais que possuo e que não possuem um controle remoto. É fácil canalizar os nomes das ramificações em um git branch -D {branch_name}, mas como faço para obter essa lista em primeiro lugar?

Por exemplo:

Eu crio uma nova filial sem um controle remoto:

$ git co -b no_upstream

Eu listo todos os meus branches, e há apenas um com controle remoto

$ git branch -a
master
* no_upstream
remotes/origin/HEAD -> origin/master
remotes/origin/master

Que comando posso executar para obter no_upstreamuma resposta?

Posso correr git rev-parse --abbrev-ref --symbolic-full-name @{u}e isso vai mostrar que não tem controle remoto:

$ git rev-parse --abbrev-ref --symbolic-full-name @{u}
error: No upstream configured for branch 'no_upstream'
error: No upstream configured for branch 'no_upstream'
fatal: ambiguous argument '@{u}': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

Mas como isso é um erro, ele não me deixa usá-lo ou canalizá-lo para outros comandos. Pretendo usar isso como um alias de script de shell git-delete-unbranchedou talvez fazer um Gem super simples comogit-branch-delete-orphans

JC Denton
fonte
Veja também stackoverflow.com/questions/4950725/…
Tomer Cohen
1
Possível duplicata de Remover ramificações locais não mais no remoto
ks1322

Respostas:

105

Recentemente, descobri git branch -vvqual é a versão "muito detalhada" do git branchcomando.

Ele produz as ramificações junto com as ramificações remotas, se houver, no seguinte formato:

  25-timeout-error-with-many-targets    206a5fa WIP: batch insert
  31-target-suggestions                 f5bdce6 [origin/31-target-suggestions] Create target suggestion for team and list on save
* 54-feedback-link                      b98e97c [origin/54-feedback-link] WIP: Feedback link in mail
  65-digest-double-publish              2de4150 WIP: publishing-state

Depois de ter essa saída bem formatada, é tão fácil quanto canalizá-la cute awkobter sua lista:

git branch -vv | cut -c 3- | awk '$3 !~/\[/ { print $1 }'

Resulta na seguinte saída:

25-timeout-error-with-many-targets
65-digest-double-publish

A cutparte apenas normaliza os dados removendo os primeiros dois caracteres (incluindo *) da saída antes de passá-los para awk.

A awkparte imprime a primeira coluna se não houver colchete na terceira coluna.

Bônus: crie um alias

Facilite a execução criando um alias em seu .gitconfigarquivo global (ou onde quer que seja):

[alias]
  local-branches = !git branch -vv | cut -c 3- | awk '$3 !~/\\[/ { print $1 }'

Importante: A barra invertida precisa ser escapada no alias ou então você terá um arquivo gitconfig inválido.

Bônus: Filtragem Remota

Se por algum motivo você tiver vários controles remotos de rastreamento para ramos diferentes, é fácil especificar com qual controle remoto você deseja verificar. Basta anexar o nome remoto ao padrão awk. No meu caso, é originpara que eu possa fazer isso:

git branch -vv | cut -c 3- | awk '$3 !~/\[origin/ { print $1 }'
Jeremy Baker
fonte
o que é isso WIP:mesmo ..? não é isso que um comando stash cria ..?
igrek
@igrek Esse é apenas um prefixo que usei na mensagem de confirmação. Nada específico do git.
Jeremy Baker
quando o faço git branch -vv, mesmo que exista um branch remoto e corresponda ao meu branch local, não veria ao [origin/branch-name]lado do resultado. Tive de diffduas listas de ramos para descobrir o que era apenas local.
Ryantuck de
git branch -vv | grep -v origin/é o suficiente para mim
não entre em pânico
2
Isso não perde os ramos remotos marcados gone?
Reid
36

git branch (sem nenhuma opção) lista apenas branches locais, mas você não sabe se eles estão rastreando um branch remoto ou não.

Normalmente, esses branches locais devem ser excluídos assim que mesclados master(como visto nesta edição do git-sweep ):

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

Isso não é tão completo quanto você deseja, mas é um começo.

Com --merged, apenas branches fundidos no commit nomeado (isto é, os branches cujos commits de ponta são alcançáveis ​​do commit nomeado) serão listados.

VonC
fonte
1
Para incluir também filiais remotas sem uma filial local, use git branch -a --merged.
Acumenus de
21

Eu tenho um problema semelhante. Quero remover todas as filiais locais que estavam rastreando filiais remotas que agora foram excluídas. Estou achando que git remote prune originfoi insuficiente para remover os galhos que quero que vão. Depois que o controle remoto for excluído, quero que o local também seja removido. Aqui está o que funcionou para mim:

Do 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 : mudei o -dpara -Dna minha configuração real, porque não quero ouvir Git reclamar de branches não mesclados. Você pode querer esta funcionalidade também. Nesse caso, basta usar em -Dvez de -dno final desse comando.

Além disso, FWIW, seu arquivo de configuração global quase sempre seria ~/.gitconfig.

(OS X Fix)

Conforme está escrito, isso não funciona no OS X por causa do uso de xargs -r(obrigado, Korny ).

O -ré para evitar que xargsseja executado git branch -dsem um nome de branch, o que resultará em uma mensagem de erro " fatal: branch name required". Se você não se importar com a mensagem de erro, pode simplesmente remover o -rargumento de xargse está tudo pronto.

No entanto, se você não quiser ver uma mensagem de erro (e realmente, quem poderia culpá-lo), você precisará de outra coisa que verifique se há um cano vazio. Se você puder usar ifne de moreutils . Você inseriria ifneantes xargs, o que deixará xargsde funcionar com dados vazios. NOTA : ifneconsidera que nada está vazio, isso inclui linhas em branco, então você ainda pode ver essa mensagem de erro. Eu não testei isso no OS X.

Aqui está essa git configlinha com ifne:

git config --global alias.prune-branches '!git remote prune origin && git branch -vv | grep '"'"': gone]'"'"' | awk '"'"'{print $1}'"'"' | ifne xargs git branch -d'
Karl Wilbur
fonte
1
É possível converter isso em um comando "git config --global ..."? Do ponto de vista de instrução / how-to-wiki-for-dummies, é fácil passar adiante em vez de dizer "Descubra seus arquivos de configuração git, use o editor para editá-los e execute comandos"
Kannan Ekanath
Observe que xargs -r não funciona no OSX
Korny
Bom saber. Não sei o que seria a opção "não executar se vazio" para o OSX.
Karl Wilbur
18

Edição tardia:

Melhor é

git for-each-ref  --format='%(refname:short) %(upstream)'  refs/heads \
| awk '$2 !~/^refs\/remotes/'

No GNU / qualquer coisa

for b in `git branch|sed s,..,,`; do
    git config --get branch.$b.remote|sed Q1 && echo git branch -D $b
done

Se mais de um punhado de branches fosse provável, haveria maneiras melhores de usar comm -23na saída de git branch|sed|sorte git config -l|sed|sort.

Jthill
fonte
1
@nmr s / git. * Q1 / test $ (git config --get branch. $ b.remote | sed q | wc -1) = 1 /
jthill
15

Isso funciona para mim:

git branch -vv | grep -v origin

(se o nome do seu controle remoto não for de origem, substitua-o).

Isso listará todos os branches que não estão rastreando um controle remoto, o que parece ser o que você está procurando.

ebr
fonte
2
Isso irá listar ramos que costumavam ter um controle remoto, mas não têm mais, mesmo se você executar git fetch --prune.
RusinaRange
2
IMO, é melhor procurar ": gone]" em vez de filtrar "origin"
fiorentinoing em
5

Eu sintetizo meu próprio comando Git para obter os origin/***: gonebranches:

git remote prune origin && git branch -vv | cut -c 3- | grep ': gone]' | awk '{print $1}' | xargs -n1 -r echo git branch -d

git remote prune origin && git branch -vv irá imprimir ramos em modo detalhado.

cut -c 3- removerá os primeiros caracteres.

grep ': gone]'irá imprimir apenas os ramos remotos que já não existem.

awk '{print $1}' irá imprimir o nome do ramo.

xargs -n1 -r echo git branch -dirá imprimir o git branch -dcomando para remover ramificações (-n1 irá gerenciar um comando por vez, -r para evitar a emissão de comandos se nenhuma ramificação estiver presente).

SUGESTÃO: remova o comando "echo" para executar os comandos em vez de imprimir apenas, deixei isso no comando para verificar os comandos antes de emitir para o git.

SUGESTÃO 2: problema git branch -Dse e somente se você tiver certeza que deseja remover ramos não mesclados

fiorentinoing
fonte
a partir disso eu fiz meu git
fiorentinoing
2
Esta é de longe a solução mais limpa que encontrei, ótimo! Nunca passei muito tempo estudando awk, não sabia que você poderia fazer isso.
adamjc
1
Este é o único que tem todos os branches que foram mesclados, excluídos ou de outra forma localmente para mim.
Máximo
2

Aqui está algo que usei no PowerShell com comentários para explicar o que ele está fazendo. Na tentativa de deixar claro o que está acontecendo, não usei nenhum comando abreviado do PowerShell (aliases). Sinta-se à vontade para compactá-lo no nível desejado de criptografia :)

$verboseList = @(git branch -vv)
foreach ($line in $verboseList)
{
    # Get the branch name
    $branch = [regex]::Match($line, "\s(\S+)\s").Captures.Groups[1].Value
    # Check if the line contains text to indicate if the remote is deleted
    $remoteGone = $line.Contains(": gone]")
    # Check if the line does not contain text to indicate it is a tracking branch (i.e., it's local)
    $isLocal = !($line.Contains("[origin/"))
    if ($remoteGone -or $isLocal)
    {
        # Print the branch name
        $branch
    }
}
Grahamesd
fonte
0

Combinando algumas das respostas existentes:

[alias]
        branch-local = !git branch -vv | cut -c 3- | egrep '([0-9a-f]{7} [^[])|(: gone\\])' | sed -E 's/(^.+[0-9a-f]{7} (\\[.+\\])?).*$/\\1/'

Por exemplo:

$ git branch-local
autogen_autoreconf      fcfb1c6 [github/autogen_autoreconf: gone]
autotools_documentation 72dc477 [github/autotools_documentation: gone]
cray-mpich-messy        2196f4b
epel-sphinx             cfda770
lammps                  07d96a7 [github/lammps: gone]
lark-error              9ab5d6c [github/lark-error: gone]
no-root2                c3894d1
openmpi-cray            69326cf [github/openmpi-cray: gone]
shellcheck-cleanup      40d90ec
travis-python36         cde072e
update_vagrant_default  4f69f47 [github/update_vagrant_default: gone]
web-docs                e73b4a8
Reid
fonte
0

Nenhuma das respostas existentes parece funcionar para mim.

Esta é minha solução para listar branches locais que não são remotos

comm -13 <(git branch -r | sed 's/origin\/\(.*\)/\1/g' | cut -c 3-) <(git branch | cut -c 3-)

E aqui está uma explicação de como funciona:

  1. Considere a lista de todos os ramos remotos (sem 'origem /' ou espaços iniciais) git branch -r | sed 's/origin\/\(.*\)/\1/g' | cut -c 3-

  2. Considere a lista de todos os ramos locais (sem asteriscos ou espaços à esquerda) git branch | cut -c 3-

  3. Compare essas duas listas e me mostre qualquer coisa na lista 2 (meus ramos locais) que não esteja na lista 1 (a lista de ramos remotos) comm -13 <(git branch -r | sed 's/origin\/\(.*\)/\1/g' | cut -c 3-) <(git branch | cut -c 3-)

comm pode receber uma combinação dos sinalizadores -1, -2 e -3 indicando de qual arquivo suprimir linhas (exclusivo para o arquivo 1, exclusivo para o arquivo 2 ou comum para ambos)

Darren Hicks
fonte