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

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

我需要做类似上面给出的事情,但我面临的问题是 $(gitbranch) 为我提供了存储库文件夹内的文件夹以及存储库中存在的分支。

这是解决这个问题的正确方法吗?或者还有其他方法可以做到吗?

谢谢

有帮助吗?

解决方案

您不应该使用的的Git分支的写作时的脚本。 Git的提供“管道”界面即明确地设计用于在脚本使用(许多现有和正常Git命令(ADD,结账,合并等的历史的实施方式)使用相同的接口)。

要的管道命令是 GIT中的for-each-REF

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

注意:除非你有其他裁判认为原因remotes/以匹配裁判的名字搜索路径的多个地方(见你不需要在远程裁判的origin/master前缀的 GIT-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

可以通过管道此输出入 SH

如果你不喜欢生成shell代码的想法,你可能放弃了一下健壮性 * ,并做到这一点:

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

* 编号名称应该是从外壳的分词安全(见的 GIT中检查-REF-格式(1))。个人而言,我会坚持与前一版本(生成shell代码);我更相信,没有什么不合适可以用它发生。 功能

既然你指定

庆典的,它支持数组,可以保证安全,仍然避免产生你的循环的胆量:

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

您如果不使用shell支持数组($@初始化和set --添加元素)可以做set -- "$@" %(refname)类似的东西。

其他提示

这是因为git branch标记有星号,e.g当前分支:

$ git branch
* master
  mybranch
$ 

所以$(git branch)膨胀至例如* master mybranch,然后*膨胀到在当前目录中的文件的列表。

我没有看到不打印在首位的星号的明显的选择;但是你可以砍掉它:

$(git branch | cut -c 3-)

在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}"

源:列出所有本地GIT中的分支不带星号

我迭代,因为它例如:

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 尽管 使用 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 中的星号 案件 构造 和 参数替换中需要使用反斜杠转义,以防止 shell 将它们解释为模式匹配字符。空格也会被转义(以防止标记化),因为您实际上是在匹配“*”。

最简单的选项,以在我看来,要记住:

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

输出:

bamboo
develop
master

grep的的-o选项(--only匹配)将输出限制为仅在输入的匹配部分。

由于既没有空间也没有*在Git的分支名称有效的,这回报分支列表,没有多余的字符。

编辑:如果你在“分离的头”状态,你需要过滤掉当前条目:

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

我最终什么事做,(由ccpizza提tr和启发)应用到你的问题:

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

(我用while循环了很多。虽然具体的东西,你肯定会想用尖头变量名[“分支”,例如,最优的,我只关注做一些与时间输入的每一订单即可。使用 '线' 在这里代替 '分支' 是点头复用性/肌肉记忆/效率。)

这是@芬兰人的答案扩展上(谢谢!),下面就让我们通过遍历的分支,而无需创建一个中间的shell脚本。这是足够强大的,只要有一个在分支名不换行:)

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

在一个子shell,除非你设置的shell变量要在当前shell访问通常是罚款,而循环运行。在这种情况下使用的过程替代扭转管:

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

如果您在此状态:

git branch -a

* master

  remotes/origin/HEAD -> origin/master

  remotes/origin/branch1

  remotes/origin/branch2

  remotes/origin/branch3

  remotes/origin/master

和运行此代码:

git branch -a | grep remotes/origin/*

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

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

done        

您会得到这样的结果:

branch1

branch2

branch3

master

<强>保持简单

使用bash脚本中循环得到分支名称的简单方法。

#!/bin/bash

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

输出:

master
other

Googlian的答案,但不使用作为

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

此用途 git的水暖命令,其被设计为脚本。这也是简单和标准。

参考: GIT中的击完成

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top