Mor*_*hit 2 git bash shell git-pull
我写了一个小脚本来自动提取/提交/推送一些个人数据.这个脚本在我的笔记本电脑上很好用(版本1.8.3.2-1),但在我的服务器上,git pull命令失败了,信号13.我运行的是1.7.9.5-1版本(这些都是ubuntu,但是服务器是12.04.4 LTS vs 13.10在笔记本电脑上),我使用ppa:git-core repo将git更新为1.9.0-1~ppa0~precision1.这仍然会给我的脚本带来同样的问题.可能的复杂性是,笔记本电脑从服务器上的裸仓库通过ssh拉出,而服务器从同一个裸仓库(到其他地方的工作副本)拉出但不使用ssh,它只有本地路径.
我可以使用以下命令在终端中重现该问题:
> git pull origin master | read msg; echo ${msg}
First, rewinding head to replay your work on top of it...
error: git-pull died of signal 13
这似乎是获取提交并检查它,但不更新主分支并使repo离开分支:
> git status
# Not currently on any branch.
nothing to commit (working directory clean)
切换回master会发出警告:
> git checkout master
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
  7220b8f Commit message
If you want to keep them by creating a new branch, this may be a good time
to do so with:
 git branch new_branch_name 7220b8f5e2648ae49d3e3095e8bf942dfc41421c
Switched to branch 'master'
认为这个问题是由于stdout是一个管道但是stderr是一个tty,我试过:
> git pull origin master 2>&1 | read msg; echo ${msg}
但这根本不做任何事情而且${msg}空洞.
工作是什么:
> git pull --quiet origin master 2>&1 | read msg; echo ${msg}
> git pull --quiet origin master
> git pull origin master
所有这些都会获取提交和更新主服务器.我想在某些情况下捕获git-pull的输出,所以当我可以修复回购时,我想知道为什么会发生这种情况.
那么为什么我不能捕获git-pull的输出,为什么它会让repo处于这种状态呢?
首先,你最大的问题是shell的东西,它与git pull它本身没什么关系.让我们做一些微不足道的事情:
$ echo foo | read msg; echo $msg
$ 
为什么$msg在这里空?答案与管道,解析和子shell有关.
shell命令的基本语法是由一系列分号和/或换行符分隔的一系列"管道".那是,给定:
a | b; c
这解析大致相同:
(a | b); c
或完全相同:
a | b
c
具体来说,b零件绑定到a零件,c零件后来.当然,添加显式括号会导致使用子shell,因此您可能会本能地意识到如果该b部件是read命令,则该c部件将不具有可用变量,因为该设置仅影响子壳,而不是外壳.
唉,删除括号无济于事.确实,这会a在主shell中运行部件 - 但是为了读取管道的输出,该b部件仍然在子shell中运行.(事实上,如果没有一些棘手的优化,当你使用括号时,该b部分在子子shell中运行:显式调用的子shell的子shell.)
这就是$msg在管道到部件退出后无法访问的原因:任何管道的右侧总是在子壳中运行.
一切都不会丢失:考虑:
$ echo foo | { read msg; echo $msg; }
foo
$ 
这里的技巧是$msg在子shell中运行整个序列.(括号也有效,语法稍微不那么笨拙:echo foo | (read msg; echo $msg).)
让我们回到最初的尝试,看看有关修补,例如:
git pull origin master | { read msg; echo ${msg}; }
这可能会做同样的事情:
First, rewinding head to replay your work on top of it...
error: git-pull died of signal 13
虽然确切的行为取决于很多东西.(特别是"倒带"消息本身表示您已将pull配置为运行rebase.)
该signal 13部分告诉我们git-pull收到SIGPIPE错误:在破损的管道上写字.破碎管可能是什么?答案应该是显而易见的,因为有一个管道用命令本身盯着我们:
git pull origin master | ...
当读者 - 右手边read msg- 在作者(LHS或git pull ...)完成之前退出时,管道"中断" ,然后作者写下新内容.因此,这表示git pull正在写入多行输出,因为该read命令读取一行然后退出(或者,在修改后的管道中,读取一行,将其写入stdout,然后退出).
如果要捕获输出,则需要捕获所有输出:
git pull origin master | while read msg; do ...; done
例如.(这不再需要大括号或括号,因为while <list> do <list> done序列作为单个语句进行解析.)或者:
git pull origin master > /tmp/script.$$
这将允许您在原始shell进程中读取捕获文件/tmp/script.$$1的内容.
在not currently on any branch发生状况,因为rebase得到了在中间中断(由于破管的错误).Rebase的工作原理是暂时离开分支,在新的匿名(未命名的"not-on-a-branch")分支上累积新提交,然后移动原始分支标签,以便现在命名新的匿名分支,以前命名的分支被放弃了.添加会--quiet停止所有输出,以便read msg等待整个git pull序列完成(之后read失败,因为毕竟没有输出).
1这真的应该用mktemp; 以上仅供参考.