如何使用bash脚本遍历所有git分支

Aru*_*hny 95 git bash git-bash

如何使用bash脚本遍历存储库中的所有本地分支.我需要迭代并检查分支和一些远程分支之间是否有任何区别.防爆

for branch in $(git branch); 
do
    git log --oneline $branch ^remotes/origin/master;
done
Run Code Online (Sandbox Code Playgroud)

我需要做上面给出的事情,但我面临的问题是$(git branch)给了我存储库文件夹中的文件夹以及存储库中存在的分支.

这是解决此问题的正确方法吗?或者还有另一种方法吗?

谢谢

Chr*_*sen 146

编写脚本时不应使用git branch.GIT中提供了一个"管道"接口是明确地设计用于在脚本使用(正常Git命令(添加,结账,合并,等)使用相同接口的许多当前的和历史的实现).

你想要的管道命令是git for-each-ref:

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

注意:您不需要remotes/在远程裁判前缀,除非你有导致其他裁判origin/master来匹配裁判的名字搜索路径多个地方(见"的符号参考的名字......." 混帐-REV-解析的指定修订部分(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
Run Code Online (Sandbox Code Playgroud)

您可以将此输出传递sh.

如果你不喜欢生成shell代码的想法,你可以放弃一些健壮性*并执行此操作:

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

*参考名称应该是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
Run Code Online (Sandbox Code Playgroud)

$@如果您没有使用支持数组的shell(set --初始化和set -- "$@" %(refname)添加元素),您可以执行类似的操作.

  • 认真.有没有更简单的方法来做到这一点? (32认同)
  • @Thayne:这个问题很老了,但是Git的人们终于解决了这个问题:`for-each-ref`现在支持所有的分支选择器,如`--merged`和`git branch`和`git tag`现在实际上根据`git for-each-ref`本身实现,至少对于列表存在的情况.(创建新的分支和标签不是,也不应该是`for-each-ref`的一部分.) (5认同)
  • 但是如果我想使用git branch的一个过滤选项,比如`--merged`,我会不得不在git branch中复制逻辑呢?必须有更好的方法来做到这一点. (4认同)
  • @wid:或者,简单来说,`git for-each-ref refs/heads --format ='%(refname)'` (4认同)
  • Simplier版本:`git for-each-ref refs / heads | 切-d / -f3-` (3认同)

Mat*_*ery 47

这是因为git branch用星号标记当前分支,例如:

$ git branch
* master
  mybranch
$ 
Run Code Online (Sandbox Code Playgroud)

所以$(git branch)扩展到eg * master mybranch,然后*扩展到当前目录中的文件列表.

我没有看到一个明显的选择,即不首先打印星号; 但你可以砍掉它:

$(git branch | cut -c 3-)
Run Code Online (Sandbox Code Playgroud)

  • 如果用双引号括起来,你可以阻止bash扩展星号 - 尽管你仍然想要从输出中删除它.从任何一点删除星号的更健壮的方法是`$(git branch | sed -es/\\*// g)`. (4认同)
  • 稍微简单的tr版本:`$(git branch | tr -d"*")` (4认同)
  • 稍微简单一点的 sed 版本: `$(gitbranch | sed 's/^..//')` (3认同)
  • 很好,我真的很喜欢你的`3-`解决方案. (2认同)

And*_*ler 11

内置的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 )
Run Code Online (Sandbox Code Playgroud)

针对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}"
Run Code Online (Sandbox Code Playgroud)

source:列出没有星号的所有本地git分支


aud*_*ude 9

for branch in $(git for-each-ref --format='%(refname:short)' refs/heads); do
    ...
done
Run Code Online (Sandbox Code Playgroud)

这使用了git 管道命令,这些命令是为脚本编写而设计的。它也很简单和标准。

参考:Git 的 Bash 补全


Djo*_*che 5

我以它为例进行迭代:

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;
Run Code Online (Sandbox Code Playgroud)