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 ci-dessus, mais le problème auquel je suis confronté est $ (branche git) me donne les dossiers à l'intérieur du dossier du référentiel ainsi que les branches présentes dans le référentiel.
Est-ce la bonne façon de résoudre ce problème? Ou existe-t-il une autre façon de procéder?
Merci
Vous ne devez pas utiliser git branch lors de l'écriture de scripts. Git fournit une "interface de plomberie" qui est explicitement conçue pour une utilisation dans les scripts (de nombreuses implémentations actuelles et historiques des commandes Git normales (ajout, extraction, fusion, etc.) utilisent cette même interface).
La commande de plomberie que vous souhaitez 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 font que Origin/master
Correspond à plusieurs endroits dans le chemin de recherche de nom de référence (voir "Un nom de référence symbolique.… ”Dans la section Spécification des révisions de git-rev-parse (1) ). Si vous essayez d'éviter explicitement toute ambiguïté, choisissez le nom de référence complet: refs/remotes/Origin/master
.
Vous obtiendrez une sortie comme celle-ci:
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 diriger cette sortie dans sh .
Si vous n'aimez pas l'idée de générer le code Shell, vous pourriez renoncer à un peu de robustesse* et faites ceci:
for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
git log --oneline "$branch" ^Origin/master
done
* Les noms de référence doivent être protégés du fractionnement des mots du shell (voir git-check-ref-format (1) ). Personnellement, je m'en tiendrai à l'ancienne version (code Shell généré); Je suis plus confiant que rien d'inapproprié ne peut s'y produire.
Puisque vous avez spécifié bash et qu'il prend en charge les tableaux, vous pouvez maintenir la sécurité et toujours éviter de générer les entrailles 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).
En effet, git branch
Marque la branche actuelle avec un astérisque, par exemple:
$ git branch
* master
mybranch
$
donc $(git branch)
se développe par exemple * master mybranch
, Puis le *
Se développe dans la liste des fichiers du répertoire en cours.
Je ne vois pas d'option évidente pour ne pas imprimer l'astérisque en premier lieu; mais vous pouvez le couper:
$(git branch | cut -c 3-)
La commande bash, mapfile
, est conçue pour cela
toutes les branches git: git branch --all --format='%(refname:short)'
toutes les branches git locales: git branch --format='%(refname:short)'
toutes les branches git distantes: git branch --remotes --format='%(refname:short)'
parcourir toutes les branches git: mapfile -t -C my_callback -c 1 < <( get_branches )
exemple:
my_callback () {
INDEX=${1}
BRANCH=${2}
echo "${INDEX} ${BRANCH}"
}
get_branches () {
git branch --all --format='%(refname:short)'
}
# mapfile -t -C my_callback -c 1 BRANCHES < <( get_branches ) # if you want the branches that were sent to mapfile in a new array as well
# echo "${BRANCHES[@]}"
mapfile -t -C my_callback -c 1 < <( get_branches )
pour la situation spécifique du PO:
#!/usr/bin/env bash
_map () {
ARRAY=${1?}
CALLBACK=${2?}
mapfile -t -C "${CALLBACK}" -c 1 <<< "${ARRAY[@]}"
}
get_history_differences () {
REF1=${1?}
REF2=${2?}
shift
shift
git log --oneline "${REF1}" ^"${REF2}" "${@}"
}
has_different_history () {
REF1=${1?}
REF2=${2?}
HIST_DIFF=$( get_history_differences "${REF1}" "${REF2}" )
return $( test -n "${HIST_DIFF}" )
}
print_different_branches () {
read -r -a ARGS <<< "${@}"
LOCAL=${ARGS[-1]?}
for REMOTE in "${SOME_REMOTE_BRANCHES[@]}"; do
if has_different_history "${LOCAL}" "${REMOTE}"; then
# { echo; echo; get_history_differences "${LOCAL}" "${REMOTE}" --color=always; } # show differences
echo local branch "${LOCAL}" is different than remote branch "${REMOTE}";
fi
done
}
get_local_branches () {
git branch --format='%(refname:short)'
}
get_different_branches () {
_map "$( get_local_branches )" print_different_branches
}
# read -r -a SOME_REMOTE_BRANCHES <<< "${@}" # use this instead for command line input
declare -a SOME_REMOTE_BRANCHES
SOME_REMOTE_BRANCHES=( Origin/master remotes/Origin/another-branch another-remote/another-interesting-branch )
DIFFERENT_BRANCHES=$( get_different_branches )
echo "${DIFFERENT_BRANCHES}"
source: Liste toutes les branches git locales sans astérisque
J'itère comme ça 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;
La réponse acceptée est correcte et devrait vraiment être l'approche utilisée, mais résoudre le problème en bash est un excellent exercice pour comprendre le fonctionnement des 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 s'étendre dans l'extension de nom de fichier (étape 8) de l'extension Shell (voir http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html )
Utilisez la construction bash while avec une commande read pour dé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 de correspondance.
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 le shell de les interpréter comme des caractères de correspondance de modèle. Les espaces sont également échappés (pour éviter la tokenisation) car vous faites littéralement correspondre '*'.
Je suggérerais $(git branch|grep -o "[0-9A-Za-z]\+")
si vos branches locales sont nommées uniquement par des chiffres, des lettres a-z et/ou A-Z
Option la plus simple à retenir à mon avis:
git branch | grep "[^* ]+" -Eo
Sortie:
bamboo
develop
master
L'option -o de Grep (--only-matching) restreint la sortie aux seules parties correspondantes de l'entrée.
Puisque ni l'espace ni * ne sont valides dans les noms de branche Git, cela retourne la liste des branches sans les caractères supplémentaires.
Edit: Si vous êtes dans état 'tête détachée' , vous devrez filtrer l'entrée actuelle:
git branch --list | grep -v "HEAD detached" | grep "[^* ]+" -oE
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 les boucles while. Alors que pour des choses particulières, vous voudrez certainement utiliser un nom de variable pointée ["branche", par exemple], la plupart du temps je ne suis concerné que par faire quelque chose avec chaque ligne d'entrée. L'utilisation de 'ligne' ici au lieu de 'branche' est un clin d'œil à la réutilisabilité/mémoire musculaire/efficacité.)
S'étendant sur la réponse de @ finn (merci!), Ce qui suit vous permettra de parcourir les branches sans créer de script Shell intermédiaire. 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 canal:
while read x ; do echo === $x === ; done < <( git for-each-ref --format='%(refname)' refs/heads )
Réponse de Googlian, mais sans utiliser pour
git for-each-ref --format='%(refname:lstrip=-1)' refs/heads/
for branch in "$(git for-each-ref --format='%(refname:short)' refs/heads)"; do
...
done
Cela utilise les commandes git plumbing , qui sont conçues pour les scripts. C'est aussi simple et standard.
Référence: Achèvement de Git's Bash
Si vous êtes dans cet état:
git branch -a
* master
remotes/Origin/HEAD -> Origin/master
remotes/Origin/branch1
remotes/Origin/branch2
remotes/Origin/branch3
remotes/Origin/master
Et vous exécutez ce code:
git branch -a | grep remotes/Origin/*
for BRANCH in `git branch -a | grep remotes/Origin/*` ;
do
A="$(cut -d'/' -f3 <<<"$BRANCH")"
echo $A
done
Vous obtiendrez ce résultat:
branch1
branch2
branch3
master
Restez simple
Le moyen simple d'obtenir le nom de la branche en boucle en utilisant le script bash.
#!/bin/bash
for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
echo "${branch/'refs/heads/'/''}"
done
Sortie:
master
other