Répertoriez toutes les succursales locales sans télécommande

91

Problème: je veux un moyen de supprimer toutes les succursales locales que j'ai qui n'ont pas de télécommande. Il est assez facile de diriger les noms des branches dans un git branch -D {branch_name}, mais comment puis-je obtenir cette liste en premier lieu?

Par exemple:

Je crée une nouvelle branche sans télécommande:

$ git co -b no_upstream

Je liste toutes mes succursales, et il n'y en a qu'une seule avec une télécommande

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

Quelle commande puis-je exécuter pour obtenir no_upstreamune réponse?

Je peux courir git rev-parse --abbrev-ref --symbolic-full-name @{u}et cela montrera qu'il n'a pas de télécommande:

$ 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>...]'

Mais comme il s'agit d'une erreur, cela ne me permettra pas de l'utiliser ou de le diriger vers d'autres commandes. J'ai l'intention de l'utiliser soit comme un script shell alias pourgit-delete-unbranched ou peut-être faire une gemme super simple commegit-branch-delete-orphans

JC Denton
la source

Réponses:

103

J'ai récemment découvert git branch -vvquelle est la version "très verbeuse" dugit branch commande.

Il sort les branches avec les branches distantes si elles existent, au format suivant:

  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

Une fois que vous avez cette sortie bien formatée, c'est aussi simple que de la passer en revue cutet awkd'obtenir votre liste:

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

Résultats dans la sortie suivante:

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

La cutpartie normalise simplement les données en supprimant les deux premiers caractères (y compris *) de la sortie avant de la passer àawk .

le awk partie imprime la première colonne s'il n'y a pas de crochets dans la troisième colonne.

Bonus: créer un alias

Rendez-le facile à exécuter en créant un alias dans votre .gitconfigfichier global (ou ailleurs):

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

Important: la barre oblique inverse doit être échappée dans l'alias, sinon vous aurez un fichier gitconfig non valide.

Bonus: filtrage à distance

Si, pour une raison quelconque, vous avez plusieurs télécommandes de suivi pour différentes succursales, il est assez facile de spécifier la télécommande que vous souhaitez vérifier. Ajoutez simplement le nom de la télécommande au modèle awk. Dans mon cas, c'est pour originque je puisse faire ceci:

git branch -vv | cut -c 3- | awk '$3 !~/\[origin/ { print $1 }'
Jeremy Baker
la source
qu'est-ce que c'est de WIP:toute façon ..? n'est-ce pas ce qu'une commande stash crée ..?
igrek
@igrek C'est juste un préfixe que j'ai utilisé dans le message de validation. Rien de spécifique à git.
Jeremy Baker
quand je le fais git branch -vv, même si une branche distante existe et correspond à ma branche locale, je ne verrais pas le à [origin/branch-name]côté du résultat. J'ai dû diffutiliser deux listes de succursales pour déterminer ce qui était uniquement local.
ryantuck
git branch -vv | grep -v origin/est assez pour moi
ne paniquez pas
2
Cela ne manque-t-il pas les branches distantes marquées gone?
Reid le
36

git branch (sans aucune option) répertorie uniquement les succursales locales, mais vous ne savez pas si elles suivent une succursale distante ou non.

Habituellement, ces branches locales doivent être supprimées une fois fusionnées dans master(comme vu dans ce numéro de git-sweep ):

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

Ce n'est pas aussi complet que vous le souhaitez, mais c'est un début.

Avec --merged, seules les branches fusionnées dans le commit nommé (c'est-à-dire les branches dont les commits tip sont accessibles à partir du commit nommé) seront listées.

VonC
la source
1
Pour inclure également des succursales distantes sans succursale locale, utilisez git branch -a --merged.
Acumenus
21

J'ai un problème similaire. Je souhaite supprimer toutes les succursales locales qui suivaient les succursales distantes qui sont maintenant supprimées. Je trouve que cela git remote prune originétait insuffisant pour supprimer les branches que je veux disparaître. Une fois la télécommande supprimée, je veux que le local disparaisse aussi. Voici ce qui a fonctionné pour moi:

De mon ~/.gitconfig:

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

Voici une git config --global ...commande pour ajouter facilement ceci comme 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'

REMARQUE : j'ai changé le -den -Ddans ma configuration actuelle, car je ne veux pas entendre Git se plaindre des branches non fusionnées. Vous pouvez également souhaiter cette fonctionnalité. Si tel est le cas, utilisez simplement à la -Dplace de-d à la fin de cette commande.

De plus, FWIW, votre fichier de configuration global le serait presque toujours ~/.gitconfig.

(Correctif OS X)

Comme écrit, cela ne fonctionne pas sur OS X en raison de l'utilisation de xargs -r(merci, Korny ).

Il -rs'agit d'empêcher xargsde fonctionner git branch -dsans nom de branche, ce qui entraînera un message d'erreur " fatal: branch name required". Si le message d'erreur ne vous dérange pas, vous pouvez simplement supprimer l' -rargument de xargset vous êtes prêt.

Cependant, si vous ne voulez pas voir de message d'erreur (et vraiment, qui pourrait vous en vouloir), vous aurez besoin de quelque chose d'autre qui vérifie si un tuyau est vide. Si vous pouvez utiliser ifne de moreutils . Vous insérez ifneavant xargs, ce qui arrêtera xargsde fonctionner avec des données vides. REMARQUE : ifneconsidère tout n'est pas vide, cela inclut des lignes vides, afin que vous puissiez toujours voir ce message d'erreur. Je n'ai pas testé cela sur OS X.

Voici cette git configligne avec 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
la source
1
Est-il possible de convertir cela en une commande "git config --global ..."? D'un point de vue instruction / how-to-wiki-for-dummies, il est facile de transmettre au lieu de dire "Découvrez vos fichiers de configuration git, utilisez l'éditeur pour le modifier puis exécutez les commandes"
Kannan Ekanath
Bon à savoir. Je ne sais pas ce que serait l'option "pas d'exécution si vide" pour OSX.
Karl Wilbur
18

Modification tardive:

Mieux vaut

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

Sur GNU / n'importe quoi

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

Si plus d'une poignée de branches étaient probables, il y aurait de meilleures façons d'utiliser comm -23la sortie de git branch|sed|sortet git config -l|sed|sort.

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

Cela fonctionne pour moi:

git branch -vv | grep -v origin

(si votre télécommande porte un nom autre que l'origine, remplacez-la).

Cela listera toutes les branches qui ne suivent pas une télécommande, ce qui ressemble à ce que vous recherchez.

ebr
la source
2
Cela listera les branches qui avaient une télécommande mais qui n'en ont plus même si vous exécutez git fetch --prune.
RusinaRange
2
OMI, il vaut mieux rechercher ": parti]" au lieu de filtrer "origine"
fiorentinoing
4

Je synthétise ma propre commande Git pour obtenir les 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 affichera les branches en mode détaillé.

cut -c 3- supprimera les tout premiers caractères.

grep ': gone]'n'imprimera que les branches distantes disparues .

awk '{print $1}' affichera le nom de la succursale.

xargs -n1 -r echo git branch -daffichera la git branch -dcommande pour supprimer les branches (-n1 gérera une commande à la fois, -r pour éviter d'émettre une commande si aucune branche n'est présente).

ASTUCE: supprimez la commande "echo" à exécuter les commandes au lieu de n'imprimer que, j'ai laissé cela dans la commande pour vérifier les commandes avant d'émettre vers git.

CONSEIL 2: problème git branch -Dsi et seulement si vous êtes sûr de vouloir supprimer les branches non fusionnées

fiorentinoing
la source
à partir de là, j'ai fait mon git
fiorentinoing
1
C'est de loin la solution la plus propre que j'ai trouvée, bien! Je n'ai jamais passé le temps à étudier awk, je ne savais pas que tu pouvais faire ça.
adamjc
2

Voici quelque chose que j'ai utilisé dans PowerShell avec des commentaires pour expliquer ce qu'il fait. Pour tenter de clarifier ce qui se passe, je n'ai utilisé aucune commande PowerShell abrégée (alias). N'hésitez pas à le compresser au niveau de crypticité souhaité :)

$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
la source
0

Combinant quelques-unes des réponses existantes:

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

Par exemple:

$ 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
la source