Question

Comment puis-je itérer à travers toutes les branches locales dans mon dépôt en utilisant un script bash. Je dois itérer et vérifier est-il une différence entre la branche et des branches éloignées. Ex

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

Je dois faire quelque chose comme indiqué ci-dessus, mais la question que je suis face est $ (branche git) me donne les dossiers dans le dossier référentiel ainsi que les branches dans le dépôt.

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

Merci

Était-ce utile?

La solution

Vous ne devriez pas utiliser git branch lors de l'écriture des scripts. Git fournit une interface « plomberie » qui est explicitement conçu pour être utilisé dans les scripts (de nombreuses implémentations actuelles et historiques des commandes Git normales (add, achat, fusion, etc.) utilisent cette même interface).

La commande de plomberie que vous voulez est git pour-chaque-ref :

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

Remarque: Vous n'avez pas besoin le préfixe remotes/ sur l'arbitre à distance, sauf si vous avez d'autres refs cette cause origin/master de correspondre à plusieurs endroits dans le chemin de recherche de nom de ref (voir dans la section des révisions Spécification de git-rev-parse (1) ). Si vous essayez d'éviter l'ambiguïté explictly, puis aller avec le nom complet ref: refs/remotes/origin/master.

Vous obtiendrez une 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 diriger cette sortie dans sh .

Si vous ne le faites pas comme l'idée de générer le code shell, vous pouvez donner un peu de robustesse * et faire ceci:

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

* Les noms Ref devraient être à l'abri de la séparation de mots de la coque (voir git-check-ref format (1) ). Personnellement, je tiendrais avec la version précédente (généré code shell); Je suis plus confiant que rien d'inapproprié peut arriver avec elle.

Depuis que vous avez spécifié bash et il prend en charge les tableaux, vous pourriez assurer la sécurité et éviter encore 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 pourriez faire quelque chose de similaire avec $@ si vous n'utilisez un shell qui prend en charge des réseaux (set -- pour initialiser et set -- "$@" %(refname) pour ajouter des éléments).

Autres conseils

Ceci est parce que les marques de git branch la branche courante par un astérisque, par exemple:.

$ git branch
* master
  mybranch
$ 

so se dilate à $(git branch) par exemple, * master mybranch, puis les dilate * à 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 au large:

$(git branch | cut -c 3-)

Le builtin bash, mapfile, est construit pour cette

toutes les branches git: git branch --all --format='%(refname:short)'

toutes les branches git locales: git branch --format='%(refname:short)'

toutes les branches git à distance: git branch --remotes --format='%(refname:short)'

itérer à travers 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 de l'OP:

#!/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 tous les locaux branches git sans un astérisque

Je itérer 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;

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

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 coquilles. L'astuce pour le faire en utilisant bash sans effectuer la manipulation de texte complémentaire, est d'assurer la sortie de la branche git n'est jamais développé dans le cadre d'une commande à exécuter par le shell. Cela empêche l'astérisque d'être jamais en expansion dans l'expansion du nom de fichier (étape 8) de l'expansion de la coque (voir http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html )

Utilisez le bash lorsque construction avec une commande de lecture pour couper la sortie de la branche git en lignes. Le « * » sera lu dans comme un caractère littéral. Utilisez une déclaration de cas pour le match, 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 les deux bash cas construction et dans la nécessité de substitution des paramètres à échapper à antislashs pour empêcher le shell de les interpréter comme des caractères correspondants de modèle. Les espaces sont également échappés (pour éviter tokens) parce que vous êtes correspondant à la lettre « * ».

l'option facile à retenir, à mon avis:

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

Sortie:

bamboo
develop
master

-o option de Grep (--only-matching) limite la sortie à seulement les parties correspondantes de l'entrée.

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

Edit: Si vous êtes dans 'tête détachée' d'état, vous aurez besoin pour filtrer l'entrée de courant:

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

Ce que je fini par faire, appliquée à votre question (et inspirée par ccpizza mentionnant tr):

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

(j'utiliser while beaucoup. Alors que pour des choses particulières que vous auriez certainement à utiliser un nom de variable pointée [ « branche », par exemple], la plupart du temps, je suis seulement concerné par faire quelque chose avec chaque ligne d'entrée. l'utilisation de 'ligne' ici au lieu de 'succursale' est un clin d'œil à la réutilisabilité / mémoire / efficacité musculaire.)

L'extension sur de @ finn de réponse, ce qui suit vous permettra de (vous! Merci) itérer sur les branches sans créer un script shell intervenant. Il est assez robuste, tant qu'il n'y a pas de nouvelles lignes dans le nom de la branche:)

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

Les pistes de boucle while dans un sous-shell, ce qui est généralement bien moins que vous configurez des variables shell que vous souhaitez accéder à l'interpréteur de commandes actuel. Dans ce cas, vous utilisez la substitution de processus pour inverser la conduite:

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

Si vous êtes à 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 obtenez ce résultat:

branch1

branch2

branch3

master

Keep it simple

La façon simple d'obtenir le nom de la branche en boucle en utilisant un script bash.

#!/bin/bash

for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
    echo "${branch/'refs/heads/'/''}" 
done

Sortie:

master
other

La réponse de Googlian, mais sans utiliser

git for-each-ref --format='%(refname:lstrip=-1)' refs/heads/
for branch in "$(git for-each-ref --format='%(refname:short)' refs/heads)"; do
    ...
done

Celui-ci utilise commandes de plomberie, qui sont conçus pour les scripts. Il est aussi simple et standard.

Référence: achèvement Bash Git

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top