Pergunta

Como posso iterar todas as ramificações locais do meu repositório usando o script bash.Preciso iterar e verificar se há alguma diferença entre a ramificação e algumas ramificações remotas.Ex

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

Preciso fazer algo como o indicado acima, mas o problema que estou enfrentando é que $(git branch) me fornece as pastas dentro da pasta do repositório junto com as ramificações presentes no repositório.

Esta é a maneira correta de resolver esse problema?Ou existe outra maneira de fazer isso?

Obrigado

Foi útil?

Solução

Você não deve usar ramo git Ao escrever scripts. Git fornece a Interface “encanamento” Isso é projetado explicitamente para uso no script (muitas implementações atuais e históricas dos comandos GIT normais (add, checkout, mescla, etc.) usam essa mesma interface).

O comando de encanamento que você deseja é Git for-Eaça-Ref:

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

Nota: você não precisa do remotes/ prefixo no referência remoto, a menos que você tenha outros árbitros que causam origin/master Para combinar com vários lugares no caminho da pesquisa de nome da ref (consulte “um nome de referência simbólica.…” Em A seção de revisões especificantes do git-rev-parse (1)). Se você está tentando evitar explicar a ambiguidade, vá com o nome completo da referência: refs/remotes/origin/master.

Você obterá saída assim:

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

Você pode colocar esta saída para sh.

Se você não gosta da ideia de gerar o código do shell, você pode desistir de um pouco de robustez* E faça isso:

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

* Os nomes de referência devem estar a salvo da divisão da palavra da concha (veja Git-check-ref-format (1)). Pessoalmente, eu continuaria com a versão anterior (código de shell gerado); Estou mais confiante de que nada inapropriado pode acontecer com isso.

Já que você especificou Bash E ele suporta matrizes, você pode manter a segurança e ainda evitar gerar as tripas do seu loop:

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

Você poderia fazer algo semelhante com $@ Se você não estiver usando um shell que suporta matrizes (set -- Para inicializar e set -- "$@" %(refname) para adicionar elementos).

Outras dicas

Isto é porque git branch marca o ramo atual com um asterisco, por exemplo:

$ git branch
* master
  mybranch
$ 

assim $(git branch) expande -se para por exemplo * master mybranch, e então o * expande -se para a lista de arquivos no diretório atual.

Não vejo uma opção óbvia para não imprimir o asterisco em primeiro lugar; Mas você pode cortar:

$(git branch | cut -c 3-)

A festa embutida, mapfile, é construído para isso

Todos os ramos git: git branch --all --format='%(refname:short)'

Todos os ramos Git local: git branch --format='%(refname:short)'

Todos os ramos git remoto: git branch --remotes --format='%(refname:short)'

itera através de todos os ramos git: mapfile -t -C my_callback -c 1 < <( get_branches )

exemplo:

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 )

Para a situação específica do 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}"

fonte: Liste todas as filiais Git local sem um asterisco

Eu itero como ele, por exemplo:

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;

eu sugeriria$(git branch|grep -o "[0-9A-Za-z]\+") Se suas filiais locais forem nomeadas apenas por letras de dígitos, AZ e/ou AZ

A resposta aceita está correta e realmente deve ser a abordagem usada, mas resolver o problema em Bash é um ótimo exercício para entender como as conchas funcionam. O truque para fazer isso usando o Bash sem executar manipulação de texto adicional é garantir que a saída do ramo Git nunca seja expandida como parte de um comando a ser executado pelo shell. Isso impede que o asterisco seja de expansão na expansão do nome do arquivo (etapa 8) da expansão da concha (ver http://tldp.org/ldp/bash-beginners-guide/html/sect_03_04.html)

Use a festa enquanto Construa com um comando de leitura para picar a saída do ramo Git em linhas. O '*' será lido como um caráter literal. Use uma declaração de caso para combiná -la, prestando atenção especial aos padrões correspondentes.

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

Os asteriscos em ambos caso Construa e na substituição de parâmetros precisam ser escapados com barras de barriga para impedir que o shell os interprete como caracteres de correspondência de padrões. Os espaços também são escapados (para evitar tokenização) porque você está literalmente correspondendo '*'.

Opção mais fácil de lembrar na minha opinião:

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

Resultado:

bamboo
develop
master

A opção -O -o de Grep (-com correspondência) restringe a saída apenas às partes correspondentes da entrada.

Como nem o espaço nem * são válidos em nomes de ramificação Git, isso retorna a lista de ramificações sem os caracteres extras.

Editar: se você estiver em Estado de 'cabeça destacada', você precisará filtrar a entrada atual:

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

O que acabei fazendo, aplicado à sua pergunta (e inspirado por ccpizza mencionando tr):

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

(Eu uso muito os loops. Enquanto para coisas específicas, você definitivamente gostaria de usar um nome de variável pontiagudo ["ramo", por exemplo], na maioria das vezes, estou preocupado apenas em fazer algo com cada um linha de entrada. Usar 'linha' aqui em vez de 'ramo' é um aceno para a reutilização/memória/eficiência muscular.)

Estendendo-se a partir da resposta de @finn (obrigado!), o seguinte permitirá que você itere nas ramificações sem criar um script de shell intermediário.É robusto o suficiente, desde que não haja novas linhas no nome do branch :)

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

O loop while é executado em um subshell, o que geralmente é bom, a menos que você esteja definindo variáveis ​​de shell que deseja acessar no shell atual.Nesse caso, você usa a substituição de processo para reverter o canal:

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

Se você está neste estado:

git branch -a

* master

  remotes/origin/HEAD -> origin/master

  remotes/origin/branch1

  remotes/origin/branch2

  remotes/origin/branch3

  remotes/origin/master

E você executa este código:

git branch -a | grep remotes/origin/*

for BRANCH in `git branch -a | grep remotes/origin/*` ;

do
    A="$(cut -d'/' -f3 <<<"$BRANCH")"
    echo $A

done        

Você receberá este resultado:

branch1

branch2

branch3

master

Mantenha simples

A maneira simples de obter o nome da filial em loop usando o script bash.

#!/bin/bash

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

Resultado:

master
other

Resposta de googliana, mas sem usar por

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

Isso usa Encanamento Git Comandos, projetados para scripts. Também é simples e padrão.

Referência: Conclusão da festa do Git

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top