gitbranch命令可以很好地用作cli命令,但当使用变量从循环或脚本运行时会失败



在创建安装脚本时,我在本地克隆了几个git repo。这是通过一个临时可用的代理完成的,该代理稍后可能可用,也可能不可用,所以我需要从远程repo创建所有远程分支作为可以切换到的本地分支。我有一种方法来提取我想要的远程repo的名称,当存储为时

[user]$ nvVar=$(git branch -r | grep -v '->' | grep -Ev 'master|spdk-1.6' | cut -d'/' -f2)

这给了我一个可以迭代的变量列表,其中包含我需要删除的分支。

[user]$ echo "$nvVar"
lightnvm
nvme-cuse
spdk

如果我手动完成所有这些操作,我会使用以下命令:

[user]$ git branch --track lightnvm origin/lightnvm
Branch lightnvm set up to track remote branch lightnvm from origin.

这很好。。。但是,当我尝试使用shell展开循环遍历变量时,我失败了。(仅供参考,如果我在$nvVar周围加引号,它不会迭代,只是尝试运行整个字符串,但失败了。我也尝试过用数组来做这件事,但也不起作用,还使用了while循环,使用git branch-r的过滤输出)

[user]$ for i in $nvVar; do git branch --track "${i}" "origin/${i}"; done

它应该产生以下git命令:

git branch --track lightnvm origin/lightnvm
git branch --track nvme-cuse origin/nvme-cuse
git branch --track spdk origin/spdk

这似乎与手动键入的相同命令相同。。但相反,我得到了这些错误:

fatal: 'lightnvm' is not a valid branch name.
fatal: 'nvme-cuse' is not a valid branch name.
fatal: 'spdk' is not a valid branch name.

这毫无意义。。。

OS:RHEL 7.6

Git版本:1.8.3.1

Bash版本:GNU Bash,版本4.2.46(2)-发布(x86_64-redhat-linux-GNU)

(编辑)显然,我捕捉到了一些特殊的角色,这些角色扰乱了命令。

有一个"^[[m〃被附加到捕获的变量…真的不知道如何在不硬编码命令的情况下消除它,我曾希望避免

找到了一个解决方案:

echo '#!/bin/bash' > gitShell
git branch -r | grep -v '->' | grep -Ev 'master|spdk-1.6' | cut -d'/' -f2 | while read remote; do
echo "git branch --track ${remote} origin/${remote}" >> gitShell
done
cat -v gitShell | sed 's/^[[m//g' > gitShell1
if /bin/bash -ex gitShell1; then
echo 'Git repos branched'
rm gitShell
rm gitShell1
fi

我只需将输出推送到一个文件中,然后使用cat-v强制隐藏的字符显示为正常字符,然后用sed过滤掉它们,然后运行新脚本。

它很笨重,但很管用。显然git返回"专用unicode字符";以响应远程查询。

感谢@Cyrus告诉我,我在原始变量中隐藏了字符。

git branch命令不是用来编写脚本的。出现此问题的原因是在分支名称中嵌入了更改颜色的文本字符串。例如,ESC[31mbranchESCm前分支使用绿色,这不是这里感兴趣的分支,但仍然为非当前分支发出各种转义序列分支案例。)

您应该使用git for-each-ref而不是git branch。这就是Git所称的管道命令:一个用于编写脚本的命令。它的输出可以很容易地进行机器解析,并且不包含任何像变色转义序列这样的陷阱。它还消除了对一些后续技巧的需要,因为它有%(...)指令,可以用来从项目中去除所需数量的前缀。

(或者,可以使用git branch,但禁用颜色输出,例如git -c color.branch=never branch。但git branch不承诺在未来对其输出进行任意更改,而git for-each-ref会这样做。)

你也可以考虑用不同的方式来解决最初的问题:;镜像克隆";,但是一旦克隆完成,就重写fetchrefspec并删除镜像配置。简言之,常规克隆和镜像克隆之间的区别在于,常规克隆复制所有1提交,而没有2分支,但镜像克隆复制所有提交以及所有分支所有其他引用,3并将remote.remote.mirror设置为true


1嗯,大多数提交,这取决于从哪些引用可以访问哪些提交。如果某些对象是隐藏的,或者只能通过reflog找到,那么你通常不会得到这些对象——但你通常也不在乎,事实上,这通常是可取的,例如,在删除意外提交的10 GB数据库之后。

2复制提交后,常规提取将分支名称转换为远程跟踪名称(例如origin/master)。最后一个git checkout步骤创建一个新的分支名称,或者如果-n有一个标记名称,则不创建。

3As与";所有提交";,这是一种礼貌的虚构:发送者可能隐藏某些引用,在这种情况下,你不会得到这些引用,而且可能也不会得到这些提交和其他对象。另一方面,为避免重新打包而进行的优化可能会意外发送不需要的对象:即使不想要,10GB的数据库也可能通过。幸运的是,reflog不是refs,所以通常不会发生这种情况。

最新更新