如何使用Bash脚本遍历我的代码库中的所有本地分支?我需要迭代并检查分支和一些远程分支之间是否有差异。
for branch in $(git branch);
do
git log --oneline $branch ^remotes/origin/master;
done
我需要像上面给出的那样做一些事情,但我面临的问题是$(git branch)会给我返回存储库文件夹内以及存储库中存在的分支。
这是解决此问题的正确方法吗?还是有其他方法可以解决它?
谢谢
编写脚本时不应使用git branch。Git提供了一个“plumbing”接口,专门为脚本设计(许多当前和历史的普通Git命令(如add、checkout、merge等)使用相同的接口)。
你需要的plumbing命令是git for-each-ref:
git for-each-ref --shell \
--format='git log --oneline %(refname) ^origin/master' \
refs/heads/
注意:除非您拥有其他引用使得origin/master
在引用名称搜索路径中多次匹配(请参见“符号引用名称...”在git-rev-parse(1)的指定修订版本章节),否则您不需要使用remotes/
前缀来表示远程引用。如果您试图明确避免歧义,请使用完整的引用名称: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)
添加元素),则可以使用类似于 $@
的方法来实现相似的功能。--merged
,那么我是否需要在git branch中重复这个逻辑呢?肯定有更好的方法来做到这一点。 - Thaynegit for-each-ref refs/heads | cut -d/ -f3-
- widgit for-each-ref refs/heads --format='%(refname)'
。 - John Gietzenfor-each-ref
现在支持所有分支选择器,例如--merged
,并且git branch
和git tag
现在实际上是通过git for-each-ref
本身来实现的,至少对于列出现有情况而言。(创建新分支和标签不应该是 for-each-ref
的一部分。) - torek这是因为git branch
会在当前分支标记一个星号,例如:
$ git branch
* master
mybranch
$
因此,$(git branch)
会扩展为例如* master mybranch
,然后*
扩展为当前目录中的文件列表。
我没有看到明显的选项可以不打印星号; 但是你可以去掉它:
$(git branch | cut -c 3-)
$(git branch | sed -e s/\\*//g)
。 - Nick$(git branch | sed 's/^..//')
该命令的作用是:从git branch
命令的输出中提取出分支名称,并去除每个分支名称前面的两个字符。可以将其翻译为:使用sed命令,从git branch
的输出中提取分支名称,并去掉它们的前两个字符。 - jdgtr
命令版:$(git branch | tr -d " *")
- ccpizza内置于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 )
#!/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}"
来源: 不带星号列出所有本地git分支
以下是要翻译的内容:for branch in $(git for-each-ref --format='%(refname:short)' refs/heads); do
...
done
这里使用了git plumbing命令,这些命令是为编写脚本而设计的,并且简单易懂。
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;
使用bash while结构和read命令将git分支输出划分为行。 '*'将被读取为文字字符。使用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 脚本循环获取分支名称的简单方法。
#!/bin/bash
for branch in $(git for-each-ref --format='%(refname)' refs/heads/); do
echo "${branch/'refs/heads/'/''}"
done
输出:
master
other
$ git branch --format="%(refname:short)"
git branch
命令具有一些额外的过滤器,例如--merged
,使用git for-each-ref
不容易实现(至少在我看来)。我个人认为最容易记住的选项是:
git branch | grep "[^* ]+" -Eo
输出结果为:
bamboo
develop
master
Grep的-o选项(--only-matching)将输出限制为输入中匹配的部分。
由于空格和*都不是Git分支名称有效的字符,因此这将返回没有额外字符的分支列表。
编辑:如果您处于'detached head' state状态,则需要过滤掉当前条目:
git branch --list | grep -v "HEAD detached" | grep "[^* ]+" -oE
如果您的本地分支仅由数字、小写字母和/或大写字母命名,我建议使用$(git branch|grep -o "[0-9A-Za-z]\+")
for b in "$(git branch)"; do git branch -D $b; done
将上述代码从英语翻译成中文: - Abhi