web-dev-qa-db-fra.com

Supprimer les branches de suivi qui ne sont plus sur la télécommande

Existe-t-il un moyen simple de supprimer toutes les branches de suivi dont l’équivalent distant n’existe plus?

Exemple:

Branches (local et distant)

  • maître
  • Origine/maître
  • Origine/correction de bug
  • Origine/bug-fix-b
  • Origine/bug-fix-c

Localement, je n'ai qu'une branche maîtresse. Maintenant, je dois travailler sur bug-fix-a, donc je le vérifie, je travaille dessus et envoie les modifications à la télécommande. Ensuite, je fais la même chose avec bug-fix-b.

Branches (local et distant)

  • maître
  • correction de bugs-a
  • bug-fix-b
  • Origine/maître
  • Origine/correction de bug
  • Origine/bug-fix-b
  • Origine/bug-fix-c

Maintenant, j'ai des branches locales master, bug-fix-a, bug-fix-b. Le responsable de la branche principale fusionnera mes modifications dans maître et supprimera toutes les branches qu’il a déjà fusionnées.

Donc, l'état actuel est maintenant:

Branches (local et distant)

  • maître
  • correction de bugs-a
  • bug-fix-b
  • Origine/maître
  • Origine/bug-fix-c

Maintenant, je voudrais appeler une commande pour supprimer des branches (dans ce cas bug-fix-a, bug-fix-b), qui ne sont plus représentées dans la télécommande dépôt.

Ce serait quelque chose comme la commande existante git remote Prune Origin, mais plutôt comme git local Prune Origin.

893
Mailo Světel

git remote Prune Origin élague les branches de suivi non sur la télécommande.

git branch --merged liste les branches qui ont été fusionnées dans la branche actuelle.

xargs git branch -d supprime les branches listées sur l'entrée standard.

Faites attention en supprimant les branches listées par git branch --merged. La liste peut inclure master ou d’autres branches que vous préférez ne pas supprimer.

Pour vous donner la possibilité de modifier la liste avant de supprimer des branches, vous pouvez procéder comme suit:

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

1012
aubreypwd

Après la commande

git fetch -p

supprime les références distantes, lorsque vous exécutez

git branch -vv

il affichera "parti" comme statut distant. Par exemple,

$ 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

Ainsi, vous pouvez écrire un script simple pour supprimer les branches locales qui sont passées à distance:

git fetch -p && for branch in `git branch -vv | grep ': gone]' | awk '{print $1}'`; do git branch -D $branch; done
444
jason.rickman

La plupart de ces réponses ne répondent pas réellement à la question initiale. J'ai fait beaucoup de recherches et this était la solution la plus propre que j'ai trouvée. Voici une version légèrement plus complète de cette réponse:

  1. Vérifiez votre branche par défaut. Habituellement git checkout master
  2. Exécuter git fetch -p && git branch -vv | awk '/: gone]/{print $1}' | xargs git branch -d

Explication:

Fonctionne en élaguant vos branches de suivi, puis en supprimant celles qui indiquent qu'elles sont "parties" dans git branch -vv.

Notes:

Si votre langue est définie sur autre chose que l'anglais, vous devrez remplacer gone par le mot approprié. Les branches qui sont uniquement locales ne seront pas touchées. Les branches qui ont été supprimées à distance mais n'ont pas été fusionnées afficheront une notification mais ne seront pas supprimées en local. Si vous souhaitez également les supprimer, remplacez -d par -D.

251
dlsso

Normalement, je ne répondrais pas à une question qui a déjà 16 réponses, mais toutes les autres réponses sont fausses et la bonne réponse est si simple. La question dit: "Existe-t-il un moyen simple de supprimer toutes les branches de suivi dont l’équivalent distant n’existe plus?"

Si "simple" signifie les supprimer en une fois, ni fragile, ni dangereux, et sans recourir à des outils dont ne disposeront pas tous les lecteurs, la bonne réponse est: non.

Certaines réponses sont simples, mais ils ne font pas ce qui a été demandé. D'autres font ce qui a été demandé, mais ne sont pas simples: ils reposent tous sur l'analyse de la sortie de Git à l'aide de commandes de manipulation de texte ou de langages de script, qui peuvent ne pas être présents sur tous les systèmes. En plus de cela, la plupart des suggestions utilisent des commandes de porcelaine, dont la sortie n'est pas conçue pour être analysée par script ("porcelaine" désigne les commandes destinées à une opération humaine; les scripts doivent utiliser les commandes de "niveau" de niveau inférieur).

Lectures complémentaires:


Si vous voulez le faire en toute sécurité, pour le cas d'utilisation de la question (branches de suivi de ramassage ramassées qui ont été supprimées sur le serveur mais existent toujours en tant que branches locales) et uniquement avec les commandes Git de haut niveau,

  • git fetch --Prune (ou git fetch -p, qui est un alias, ou git Prune remote Origin qui fait la même chose sans récupérer et qui n'est probablement pas ce que vous voulez la plupart du temps).
  • Notez toutes les branches distantes signalées comme supprimées. Ou, pour les retrouver plus tard, git branch -v (toute branche de suivi orpheline sera marquée "[partie]").
  • git branch -d [branch_name] sur chaque branche de suivi orpheline

(ce que proposent certaines des autres réponses).

Si vous souhaitez créer un script pour une solution, alors for-each-ref est votre point de départ, comme dans réponse de Mark Longair ici et cette réponse à une autre question , mais je peux ne voyez pas un moyen de l'exploiter sans écrire une boucle de script Shell, ni utiliser xargs ou quelque chose comme ça.


Explication de fond

Pour comprendre ce qui se passe, vous devez comprendre que, dans le cas du suivi de branches, vous n’avez pas une branche, mais trois. (Et rappelez-vous que "branche" signifie simplement un pointeur sur un commit.)

Avec une branche de suivi feature/X, le référentiel distant (serveur) aura cette branche et l’appellera feature/X. Votre référentiel local a une branche remotes/Origin/feature/X qui signifie "C'est ce que la télécommande m'a dit que sa fonction était/la branche X était, la dernière fois que nous avons parlé", et enfin, le référentiel local a une branche feature/X qui pointe vers votre dernier commit et est configuré pour "suivre" remotes/Origin/feature/X, ce qui signifie que vous pouvez tirer et pousser pour les maintenir alignés.

À un moment donné, quelqu'un a supprimé le feature/X de la télécommande. À partir de ce moment, vous restez avec votre feature/X local (ce que vous ne voulez probablement plus, car le travail sur la fonctionnalité X est probablement terminé), et votre remotes/Origin/feature/X qui est certainement inutile car son seul but était de se souvenir de l'état de la branche du serveur.

Et Git vous laissera nettoyer automatiquement le remotes/Origin/feature/X redondant - c'est ce que git fetch --Prune fait - mais pour une raison quelconque, il ne vous permet pas de supprimer automatiquement votre propre feature/X... même si votre feature/X contient toujours les informations de suivi orphelines, il dispose donc des informations lui permettant d'identifier les anciennes branches de suivi entièrement fusionnées. (Après tout, il peut vous donner l'information qui vous permet de faire l'opération à la main vous-même.)

144
Andrew Spencer

J'ai trouvé la réponse ici: Comment puis-je supprimer toutes les branches git qui ont été fusionnées?

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

Assurez-vous de garder le maître

Vous pouvez vous assurer que master, ou toute autre branche, ne soit pas supprimé en ajoutant un autre grep après le premier. Dans ce cas, vous iriez:

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

Donc si nous voulions garder master, develop et staging par exemple, nous irions:

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

Faites-en un alias

Comme il est un peu long, vous voudrez peut-être ajouter un alias à votre .zshrc ou .bashrc. Le mien s'appelle gbpurge (pour 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'

Rechargez ensuite votre .bashrc ou .zshrc:

. ~/.bashrc

ou

. ~/.zshrc
49
karlingen

La solution semble être ici - https://stackoverflow.com/a/1072178/133986

En bref, git remote Prune fait la magie

48
Mailo Světel

Supprimez toutes les branches qui ont été fusionnées dans le maître, mais n'essayez pas de supprimer le maître lui-même:

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

ou ajoutez un alias:

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

Explication:

git checkout master branche de commande principale

git pull Origin master s'assurer que la branche locale a fusionné toutes les modifications à distance

git fetch -p supprime les références aux branches distantes supprimées

git branch -d $(git branch master --merged | grep master -v) supprimer toutes les branches qui ont été fusionnées dans le maître, mais n'essayez pas de supprimer le maître lui-même

29
cs01

Solution Windows

Pour 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 $_}

Explication

git checkout master passe à la branche principale

git remote update Origin --Prune élague les branches distantes

git branch -vv obtient une sortie détaillée de toutes les branches ( référence git )

Select-String -Pattern ": gone]" ne récupère que les enregistrements où ils ont été supprimés.

% { $_.toString().Trim().Split(" ")[0]} récupère le nom de la branche

% {git branch -d $_} supprime la branche

28
chris31389

Le motif correspondant à "disparu" dans la plupart des autres solutions m'a un peu effrayé. Pour plus de sécurité, ceci utilise le drapeau --format pour extraire le numéro de chaque branche statut de suivi en amont .

J'avais besoin d'une version compatible avec Windows, donc cela supprime toutes les branches répertoriées comme "ayant disparu" à l'aide de Powershell:

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

La première ligne indique le nom des branches locales dont la branche en amont est "partie". La ligne suivante supprime les lignes vierges (qui sont générées pour les branches qui ne sont pas "parties"), puis le nom de la branche est transmis à la commande pour supprimer la branche.

18
Patrick Quirk
git fetch -p

Cela supprimera toutes les branches qui ne sont pas suivies à distance.

16
ckirksey3

Pourrait être utile à certains, simple ligne à effacer toutes les branches locales, sauf maître et développer

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

Ceci supprimera toutes les branches locales fusionnées sauf la référence principale locale et celle actuellement utilisée:

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

Et cela supprimera toutes les branches ayant déjà été supprimées du référentiel distant référencé par "Origin", mais sont toujours disponibles localement dans "télécommandes/Origin".

git remote Prune Origin
13
pabloa98

Je ne pense pas qu'il existe une commande intégrée pour le faire, mais vous pouvez effectuer les opérations suivantes en toute sécurité:

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

Lorsque vous utilisez -d, git refusera de supprimer la branche à moins qu'elle ne soit complètement fusionnée dans HEAD ou sa branche de suivi à distance en amont. Donc, vous pouvez toujours faire une boucle sur la sortie de git for-each-ref et essayer de supprimer chaque branche. Le problème avec cette approche est que je suppose que vous ne voulez probablement pas que bug-fix-d soit supprimé simplement parce que Origin/bug-fix-d contient son historique. Au lieu de cela, vous pouvez créer un script du type suivant:

#!/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

Attention: je n'ai pas testé ce script - utilisez-le avec précaution ...

12
Mark Longair

Encore une autre réponse pour la pile, tirant beaucoup de réponse de Patrick (ce qui me plaît car cela semble éliminer toute ambiguïté quant à la correspondance entre gone] et le git branch sortie) mais en ajoutant un * nix plié:

git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname)%(end)" \
  | sed 's,^refs/heads/,,;/^$/d' \
  | xargs git branch -D

J'ai ce résumé dans un script git-gone sur mon chemin:

#!/usr/bin/env bash

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

get_gone() {
  git branch --list --format "%(if:equals=[gone])%(upstream:track)%(then)%(refname)%(end)" \
    | sed 's,^refs/heads/,,;/^$/d'

}

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

main "${@}"

NB - L’option --format semble être relativement nouvelle; Je devais mettre à jour git de 2.10.quelque chose à 2.16.3 pour l'obtenir.

9
bxm

Basé sur les informations ci-dessus, cela a fonctionné pour moi:

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

Il supprime toutes les branches locales avec ': gone] ' sur distant.

6
Joost den Boer
grep gone <(git branch -v) | cut -d ' ' -f 3 | xargs git branch -d

La commande ci-dessus peut être utilisée pour récupérer des branches qui sont fusionnées et supprimées à distance et supprime la branche locale qui n’est plus disponible en distance.

6
thiruclassic

Rien de tout cela n'était vraiment juste pour moi. Je voulais quelque chose qui purge toutes les branches locales qui suivaient une branche distante, sur Origin, où la branche distante a été supprimée (gone). Je ne voulais pas supprimer les branches locales qui n'étaient jamais configurées pour suivre une branche distante (c'est-à-dire: mes branches de développement locales). De plus, je voulais une ligne simple qui utilise simplement git, ou d’autres outils CLI simples, plutôt que d’écrire des scripts personnalisés. J'ai fini par utiliser un peu de grep et awk pour créer cette commande simple.

C'est finalement ce qui s'est retrouvé dans mon ~/.gitconfig:

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

Voici une commande git config --global ... pour l'ajouter facilement en tant que 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: Dans la commande config, j'utilise l'option -d pour git branch plutôt que -D, comme dans ma configuration actuelle. J'utilise -D parce que je ne veux pas entendre Git se plaindre des branches non fusionnées. Vous voudrez peut-être aussi cette fonctionnalité. Si c'est le cas, utilisez simplement -D au lieu de -d à la fin de cette commande de configuration.

5
Karl Wilbur
git pull -p 

Si nous utilisons -p avec pull, cela supprimera toutes les branches supprimées du local.

2
Jamsheer

Tirant fortement d'un nombredeautreréponses ici, j'ai fini avec ce qui suit (git 2.13 et ci-dessus seulement, je crois), qui devrait fonctionner sur tout shell de type UNIX:

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

Cela utilise notamment for-each-ref au lieu de branch (car branch est une commande "porcelaine" conçue pour une sortie lisible par l'homme, et non pour un traitement par machine) et utilise son argument --Shell obtenir une sortie correctement échappée (cela nous permet de ne pas nous inquiéter des caractères dans le nom de la référence).

2
gsnedders

Basé sur Astuce Git: supprimer les anciennes branches locales , qui ressemble à solution de jason.rickman J'ai mis en place une commande personnalisée appelée git gone en utilisant 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 combine l'élagage et la liste des branches "parties":

$ 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

Vous pouvez ensuite appuyer sur la gâchette en utilisant git gone -d ou git gone -D.

Remarques

  • L'expression régulière que j'ai utilisée est "$BRANCH/.*: gone]"$BRANCH serait normalement Origin. Cela ne fonctionnera probablement pas si votre sortie Git est localisée en français, etc.
  • Sebastian Wiesner l'a également porté à Rust pour les utilisateurs de Windows. Celui-ci est aussi appelé git gone .
2
Eugene Yokota

Je suis venu avec ce script bash. Il garde toujours les branches 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
BrunoLM

Cela a fonctionné pour moi:

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

J'utilise une méthode courte pour faire le tour, je vous recommande de faire la même chose car cela pourrait vous faire gagner quelques heures et vous donner plus de visibilité

Il suffit d’ajouter l’extrait suivant dans votre fichier .bashrc (.bashprofile on macos).

git-cleaner() { git fetch --all --Prune && git branch --merged | grep -v -E "\bmaster|preprod|dmz\b" | xargs -n 1 git branch -d ;};
  1. Chercher toutes les télécommandes
  2. N'obtenez que les branches fusionnées de git
  3. Supprimer de cette liste les branches "protected/important"
  4. Supprimer le reste (par exemple, les branches propres et fusionnées)

Vous devrez éditer la regex grep afin de l’adapter à vos besoins (ici, cela empêche la suppression de master, preprod et dmz)

0
Ben Cassinat

Je ne sais pas pour combien de temps, mais j'utilise maintenant git-up, qui s'en occupe.

Je fais git up et il commence à dépister de nouvelles branches et supprime les anciennes.

Soyons clairs: ce n'est pas une commande git prête à l'emploi - https://github.com/aanand/git-up

En outre, il bloque également l’arbre encrassé et fait des rebases avec juste git up.

J'espère que ça va être utile pour quelqu'un

0
Mailo Světel

Voici une solution que j'utilise pour le poisson Shell. Testé sur Mac OS X 10.11.5, fish 2.3.0 et 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

Quelques notes:

Assurez-vous de définir le base_branch correct. Dans ce cas, j'utilise develop comme branche de base, mais cela pourrait être n'importe quoi.

Cette partie est très importante: grep -v "\(master\|$base_branch\|\*\)". Cela garantit que vous ne supprimez pas le maître ou votre branche de base.

J'utilise git branch -d <branch> à titre de précaution supplémentaire, afin de ne pas supprimer les branches qui n'ont pas été complètement fusionnées avec HEAD en amont ou actuel.

Un moyen simple de tester consiste à remplacer git branch -d $f par echo "will delete $f".

Je suppose que je devrais également ajouter: UTILISER AT VOTRE PROPRE RISQUE!

0
lps