如何使用bash脚本迭代所有git分支

如何使用bash脚本遍历存储库中的所有本地分支。 我需要迭代并检查分支和一些远程分支之间是否有任何区别。 防爆
for branch in $(git branch); 
do
    git log --oneline $branch ^remotes/origin/master;
done
我需要做上面给出的事情,但我面临的问题是$(git branch)给了我存储库文件夹中的文件夹以及存储库中存在的分支。 这是解决此问题的正确方法吗?或者还有另一种方法吗? 谢谢     
已邀请:
编写脚本时不应使用git branch。 Git提供了一个明确设计用于脚本的“管道”接口(普通Git命令的许多当前和历史实现(添加,检出,合并等)使用相同的接口)。 你想要的管道命令是git for-each-ref:
git for-each-ref --shell 
  --format='git log --oneline %(refname) ^origin/master' 
  refs/heads/
注意:您不需要远程引用上的
remotes/
前缀,除非您有其他引用
origin/master
匹配引用名称搜索路径中的多个位置(请参阅git-rev的“指定修订”部分中的“符号引用名称。...” -parse(1))。如果你试图明确地避免含糊不清,那么请使用完整的引用名称:
refs/remotes/origin/master
。 你会得到这样的输出:
git log --oneline 'refs/heads/master' ^origin/master
git log --oneline 'refs/heads/other' ^origin/master
git log --oneline 'refs/heads/pu' ^origin/master
您可以将此输出传递给sh。 如果你不喜欢生成shell代码的想法,你可以放弃一些健壮性*并执行此操作:
for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
    git log --oneline "$branch" ^origin/master
done
* 引用名称应该是shell的单词拆分安全(参见git-check-ref-format(1))。我个人会坚持使用以前的版本(生成的shell代码);我更有信心不会发生任何不适当的事情。 由于您指定了bash并且它支持数组,因此您可以保持安全并仍然避免生成循环的内容:
branches=()
eval "$(git for-each-ref --shell --format='branches+=(%(refname))' refs/heads/)"
for branch in "${branches[@]}"; do
    # …
done
如果你没有使用支持数组的shell(
set --
初始化和
set -- "$@" %(refname)
添加元素),你可以用ѭ8做类似的事情。     
这是因为
git branch
用星号标记当前分支,例如:
$ git branch
* master
  mybranch
$ 
所以
$(git branch)
扩大到例如
* master mybranch
,然后
*
扩展到当前目录中的文件列表。 我没有看到一个明显的选择,即不首先打印星号;但你可以砍掉它:
$(git branch | cut -c 3-)
    
我以它为例进行迭代:
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;
    
我会建议
$(git branch|grep -o "[0-9A-Za-z]+")
如果您的本地分支仅由数字,a-z和/或A-Z字母命名     
接受的答案是正确的,实际上应该是使用的方法,但在bash中解决问题是理解shell如何工作的一个很好的练习。使用bash执行此操作而不执行其他文本操作的技巧是确保git分支的输出永远不会作为shell执行的命令的一部分进行扩展。这可以防止星号在shell扩展的文件名扩展(步骤8)中扩展(请参阅http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html) 在构造时使用bash with read命令将git branch输出切换为行。 '*'将作为文字字符读入。使用case语句来匹配它,特别注意匹配的模式。
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
bash case构造和参数替换中的星号需要使用反斜杠进行转义,以防止shell将它们解释为模式匹配字符。空格也被转义(以防止标记化),因为你实际上匹配'*'。     
内置的bash,
mapfile
,就是为此而建的 所有的git分支:
git branch --all --format='%(refname:short)'
所有当地的git分支:
git branch --format='%(refname:short)'
所有远程git分支:
git branch --remotes --format='%(refname:short)'
遍历所有git分支:
mapfile -t -C my_callback -c 1 < <( get_branches )
例:
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 )
针对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:列出没有星号的所有本地git分支     
我最终做了什么,适用于你的问题(并且受到ccpizza提及
tr
的启发):
git branch | tr -d ' *' | while IFS='' read -r line; do git log --oneline "$line" ^remotes/origin/master; done
(我使用while循环很多。虽然对于特定的东西你肯定想要使用一个有针对性的变量名[例如]“分支”),大多数时候我只关心每行输入做一些事情。 'line'在这里而不是'branch'是对可重用性/肌肉记忆/效率的点头。)     
在我看来,最简单的选择:
git branch | grep "[^* ]+" -Eo
输出:
bamboo
develop
master
Grep的-o选项(--only-matching)将输出限制为仅输入的匹配部分。 由于空格和*在Git分支名称中都不起作用,因此返回没有额外字符的分支列表。 编辑:如果您处于'分离头'状态,则需要过滤掉当前条目:
git branch --list | grep -v "HEAD detached" | grep "[^* ]+" -oE
    
从@finn的答案扩展(谢谢!),以下内容将允许您迭代分支而无需创建插入的shell脚本。它足够健壮,只要分支名称中没有换行符:)
git for-each-ref --format='%(refname)' refs/heads  | while read x ; do echo === $x === ; done
while循环在子shell中运行,除非您在当前shell中设置要访问的shell变量,否则这通常很好。在这种情况下,您使用进程替换来反转管道:
while read x ; do echo === $x === ; done < <( git for-each-ref --format='%(refname)' refs/heads )
    

要回复问题请先登录注册