Comment parcourir toutes les branches git en utilisant le script bash

Comment puis-je parcourir toutes les branches locales de mon référentiel en utilisant le script bash. J'ai besoin d'itérer et de vérifier s'il y a une différence entre la branche et certaines branches distantes. Ex

for branch in $(git branch); 
do
    git log --oneline $branch ^remotes/origin/master;
done

Je dois faire quelque chose comme donné ci-dessus, mais le problème auquel je suis confronté est $(git branch) me donne les dossiers dans le dossier du référentiel avec les branches présentes dans le référentiel.

Est-ce la bonne façon de résoudre ce problème? Ou est-il une autre façon de le faire il?

Merci

78
demandé sur Arun P Johny 2010-10-02 19:33:20

8 réponses

, Vous ne devez pas utiliser git branch lors de l'écriture de scripts. Git fournit une interface "plomberie" {[14] } explicitement conçue pour être utilisée dans les scripts (nombreuses implémentations actuelles et historiques de commandes git normales (add, checkout, merge, etc. utilisez cette même interface).

La commande de plomberie que vous voulez est git for-each-ref :

git for-each-ref --shell \
  --format='git log --oneline %(refname) ^origin/master' \
  refs/heads/

REMARQUE: Vous n'avez pas besoin du préfixe remotes/ sur la référence distante sauf si vous avez d'autres références qui causent origin/master à faites correspondre plusieurs endroits dans le chemin de recherche du nom de référence (voir " un nom de référence symbolique. ..." dans la section de spécification des révisions de git-rev-parse(1)). Si vous essayez d'éviter explicitement l'ambiguïté, alors allez avec le nom de référence complet: refs/remotes/origin/master.

Vous obtiendrez la sortie comme ceci:

git log --oneline 'refs/heads/master' ^origin/master
git log --oneline 'refs/heads/other' ^origin/master
git log --oneline 'refs/heads/pu' ^origin/master

, Vous pouvez rediriger cette sortie dans sh.

Si vous n'aimez pas l'idée de générer le code shell, vous pourriez abandonner un peu de Robustesse* et faire ce:

for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
    git log --oneline "$branch" ^origin/master
done

* Les noms de Ref doivent être à l'abri du fractionnement des mots du shell (voir git-check-ref-format(1)). Personnellement, je m'en tiendrais à l'ancienne version (Code shell généré); je suis plus confiant que rien d'inapproprié ne peut arriver avec elle.

Puisque vous avez spécifié bash et qu'il prend en charge les tableaux, vous pouvez maintenir la sécurité et éviter de générer les tripes de votre boucle:

branches=()
eval "$(git for-each-ref --shell --format='branches+=(%(refname))' refs/heads/)"
for branch in "${branches[@]}"; do
    # …
done

Vous pouvez faire quelque chose de similaire avec $@ Si vous n'utilisez pas un shell qui prend en charge les tableaux (set -- pour initialiser et set -- "$@" %(refname) pour ajouter des éléments).

129
répondu Chris Johnsen 2014-03-10 19:12:38

C'est parce que git branch marque la branche actuelle avec un astérisque, par exemple:

$ git branch
* master
  mybranch
$ 

, Donc $(git branch) étend, par exemple * master mybranch, puis le * étend à la liste des fichiers dans le répertoire courant.

Je ne vois pas une option évidente pour ne pas imprimer l'astérisque en premier lieu; mais vous pouvez le couper:

$(git branch | cut -c 3-)
42
répondu Matthew Slattery 2010-10-02 15:49:47

Je suggère $(git branch|grep -o "[0-9A-Za-z]\+") si vos branches locales sont nommées par des chiffres, des lettres A-z et/ou A-Z uniquement

4
répondu Xin Guo 2012-07-20 03:43:00

La réponse acceptée est correcte et devrait vraiment être l'approche utilisée, mais résoudre le problème dans bash est un excellent exercice pour comprendre comment fonctionnent les shells. L'astuce pour faire cela en utilisant bash sans effectuer de manipulation de texte supplémentaire, est de s'assurer que la sortie de la branche git n'est jamais développée dans le cadre d'une commande à exécuter par le shell. Cela empêche l'astérisque de se développer dans l'expansion du nom de fichier (étape 8) de l'expansion du shell (voir http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html )

Utilisez la construction bash while avec une commande read pour couper la sortie de la branche git en lignes. Le ' * ' sera lu comme un caractère littéral. Utilisez une déclaration de cas pour la faire correspondre, en accordant une attention particulière aux modèles correspondants.

git branch | while read line ; do                                                                                                        
    case $line in
        \*\ *) branch=${line#\*\ } ;;  # match the current branch
        *) branch=$line ;;             # match all the other branches
    esac
    git log --oneline $branch ^remotes/origin/master
done

Les astérisques dans la construction bash case et dans la substitution de paramètres doivent être échappés avec des barres obliques inverses pour empêcher l'interprétation du shell eux comme motif correspondant caractères. Les espaces sont également échappés (pour empêcher la tokenisation) car vous correspondez littéralement à'*'.

4
répondu BitByteDog 2016-01-30 08:39:58

J'itère comme par exemple:

for BRANCH in `git branch --list|sed 's/\*//g'`;
  do 
    git checkout $BRANCH
    git fetch
    git branch --set-upstream-to=origin/$BRANCH $BRANCH
  done
git checkout master;
4
répondu Djory Krache 2016-03-21 11:40:51

Option la plus facile à retenir à mon avis:

git branch | grep "[^* ]+" -Eo

Sortie:

bamboo
develop
master

L'option-o de Grep (--only-matching) limite la sortie aux seules parties correspondantes de l'entrée.

Puisque ni l'espace ni * ne sont valides dans les noms de branches Git, cela renvoie la liste des branches sans les caractères supplémentaires.

Edit: si vous êtes dans ' detached head ' state , vous devrez filtrer l'entrée actuelle:

git branch --list | grep -v "HEAD detached" | grep "[^* ]+" -oE

2
répondu staafl 2017-08-01 14:00:11

Ce que j'ai fini par faire, appliqué à votre question( et inspiré par ccpizza mentionnant tr):

git branch | tr -d ' *' | while IFS='' read -r line; do git log --oneline "$line" ^remotes/origin/master; done

(j'utilise beaucoup de boucles while. Alors que pour des choses particulières, vous voudriez certainement utiliser un nom de variable pointu ["branch", par exemple], la plupart du temps, Je ne suis préoccupé que par faire quelque chose avec chaque Ligne d'entrée. Utiliser 'line' ici au lieu de 'branch' est un clin d'œil à la réutilisabilité/mémoire musculaire/efficacité.)

2
répondu Jan Kyu Peblik 2018-01-30 18:36:39

S'étendant sur la réponse de @finn (merci!), ce qui suit vous permettra d'itérer sur les branches sans créer un script shell intervenant. C'est assez robuste, tant qu'il n'y a pas de nouvelle ligne dans le nom de la branche:)

git for-each-ref --format='%(refname)' refs/heads  | while read x ; do echo === $x === ; done

La boucle while s'exécute dans un sous-shell, ce qui est généralement bien sauf si vous définissez des variables shell auxquelles vous souhaitez accéder dans le shell actuel. Dans ce cas, vous utilisez la substitution de processus pour inverser le tuyau:

while read x ; do echo === $x === ; done < <( git for-each-ref --format='%(refname)' refs/heads )
1
répondu Chris Cogdon 2017-10-23 22:31:09