Cómo iterar a través de todas las ramas de git usando el script bash

¿Cómo puedo iterar a través de todas las ramas locales en mi repositorio utilizando el script bash? Necesito repetir y comprobar si hay una diferencia entre una rama y algunas sucursales remotas. Ex

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

Necesito hacer algo como el anterior, pero el problema que encontré es $ (git branch), me da carpetas dentro de la carpeta del repositorio junto con las sucursales presentes en el repositorio.

¿Es esta la manera correcta de resolver este problema? ¿O hay otra manera de hacer esto?

Gracias

79
02 окт. Arun P Johny establece 02 oct. 2010-10-02 18:33 '10 a las 18:33 2010-10-02 18:33
@ 9 respuestas

No debes usar la rama git al escribir scripts. git proporciona una interfaz de "fontanería" que está explícitamente diseñada para su uso en scripts (muchas implementaciones actuales e históricas de los comandos comunes de git (agregar, desproteger, combinar, etc.) usan la misma interfaz).

El equipo de plomería que desea es git for-every-ref:

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

Nota: no necesita los remotes/ prefijo en el enlace de referencia remota a menos que tenga otros enlaces que llamen a origin/master para que coincidan con varios lugares en la ruta de búsqueda del nombre del enlace (consulte "El nombre simbólico del enlace ..." en la sección "Especificación de cambios "en la sección git -rev-parse (1) ). Si está tratando de evitar claramente la ambigüedad, vaya con el nombre completo ref: refs/remotes/origin/master .

Obtendrá el resultado de la siguiente manera:

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

Puede pasar esta salida a sh.

Si no te gusta la idea de generar código de shell, puedes optar por cierta confiabilidad * y hacer esto:

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

* Los nombres de los enlaces deben estar a salvo del ajuste de texto (consulte git -check-ref-format (1) ). Personalmente, me quedaría con la versión anterior (código de shell generado); Estoy seguro de que nada le puede pasar.

Como ha especificado bash y es compatible con matrices, puede mantener la seguridad y aún así evitar generar las agallas de su ciclo:

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

Puede hacer algo similar con $@ si no usa un shell que admita arrays ( set -- para inicialización y set -- "$@" %(refname) para agregar elementos).

132
03 окт. Respuesta dada por Chris Johnsen el 3 de octubre. 2010-10-03 00:24 '10 a las 0:24 2010-10-03 00:24

Esto se debe al hecho de que la git branch marca la rama actual con un asterisco, por ejemplo:

 $ git branch * master mybranch $ 

así que $(git branch) expande a, por ejemplo, * master mybranch , y luego * expande a la lista de archivos en el directorio actual.

border=0

No veo la opción obvia de no imprimir un asterisco en primer lugar; pero puedes apagarlo:

 $(git branch | cut -c 3-) 
43
02 окт. respuesta dada por Matthew Slattery 02 oct. 2010-10-02 18:49 '10 a las 6:49 pm 2010-10-02 18:49

Repito así, por ejemplo:

 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; 
5
21 марта '16 в 14:40 2016-03-21 14:40 la respuesta la da Djory Krache el 21 de marzo de 2016 a las 14:40 2016-03-21 14:40

La respuesta aceptada es correcta y realmente debería usarse en un enfoque, pero resolver el problema en bash es un excelente ejercicio para comprender cómo funcionan los shells. El truco para usar bash sin manipulación de texto adicional es asegurarse de que la salida de la rama git nunca se expanda como parte del comando que ejecutará el shell. Esto evita que el asterisco se expanda al expandir el nombre de archivo (paso 8) de la extensión de shell (consulte http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html )

Use la construcción bash con el comando de lectura para dividir la salida de las ramas de git en líneas. "*" Será considerado un carácter alfabético. Utilice el argumento del caso para que coincida, prestando especial atención a los patrones correspondientes.

 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 

Los asteriscos en la construcción del caso de bash y en la sustitución de parámetros deben escaparse utilizando barras diagonales inversas para que el shell no los interprete como caracteres de coincidencia de patrones. Los espacios también se escapan (para evitar la tokenización), porque literalmente se hace coincidir el "*".

4
30 янв. La respuesta se da a BitByteDog el 30 de enero. 2016-01-30 11:32 '16 a las 11:32 2016-01-30 11:32

mapfile bash, mapfile , creado para esto

todas las ramas de git: git branch --all --format='%(refname:short)'

todas las ramas locales de git: git branch --format='%(refname:short)'

todas las ramas de git eliminadas: git branch --remotes --format='%(refname:short)'

ir a través de todas las ramas de git: mapfile -t -c my_callback -c 1 < <( get_branches )

ejemplo:

 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 una situación particular 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}" 

fuente: una lista de todas las sucursales locales de git sin un asterisco

4
13 нояб. La respuesta la da Andrew Miller el 13 de noviembre. 2018-11-13 01:30 '18 a la 1:30 2018-11-13 01:30

Sugeriría $(git branch|grep -o "[0-9A-Za-z]\+") si sus sucursales locales se llaman solo números, az y / o letras AZ

4
20 июля '12 в 6:43 2012-07-20 06:43 La respuesta la da Xin Guo el 20 de julio de '12 a las 6:43 am 2012-07-20 06:43

Lo que finalmente hice se aplicó a tu pregunta (e inspiré ccpizza al mencionar tr ):

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

(A menudo utilizo bucles while. Aunque para ciertas cosas definitivamente desea usar la variable especificada con el nombre [por ejemplo, "rama"), la mayoría de las veces solo hago algo que ver con cada línea de entrada. La "línea" está aquí, no la "rama" (un signo de reutilización / memoria muscular / eficiencia).

3
30 янв. La respuesta es dada a Jan Kyu Peblik el 30 de enero. 2018-01-30 21:36 '18 a las 21:36 2018-01-30 21:36

La opción más fácil de recordar en mi opinión:

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

Conclusión:

 bamboo develop master 

El parámetro grep -o (--only-matching) limita la salida solo a las partes apropiadas de la entrada.

Dado que ni el espacio ni el * son válidos en los nombres de las sucursales de Git, esto devuelve una lista de sucursales sin caracteres adicionales.

Editar: Si está en el estado de "cabeza desconectada" , necesita filtrar el registro actual:

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

2
01 авг. la respuesta se da staafl 01 ago. 2017-08-01 17:00 '17 a las 5 pm 2017-08-01 17:00

Ampliando la respuesta @finn (¡gracias!), Lo siguiente le permitirá recorrer las ramas sin crear un shell de script intermedio. Es lo suficientemente fuerte si no hay nuevas líneas en el nombre de la rama :)

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

El bucle while funciona en una subshell, que generalmente está bien si no configura los shells variables a los que desea acceder en el shell actual. En este caso, utiliza la sustitución de procesos para la transformación inversa:

 while read x ; do echo === $x === ; done < <( git for-each-ref --format='%(refname)' refs/heads ) 
1
24 окт. respuesta dada por Chris Cogdon el 24 de octubre 2017-10-24 01:31 '17 a la 1:31 2017-10-24 01:31

Otras preguntas sobre las etiquetas de o Haz una pregunta