【问题标题】:Why does git-pull fail with signal 13?为什么 git-pull 会因信号 13 而失败?
【发布时间】:2014-03-18 22:03:43
【问题描述】:

设置

我编写了一个小脚本来自动提取/提交/推送一些个人数据。该脚本在我的笔记本电脑(版本 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~precise1。这仍然给我的脚本带​​来了同样的问题。复杂的可能是,笔记本电脑通过 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 捕获输出,这样虽然我可以修复 repo,但我想知道为什么会这样。

那么为什么我不能捕获 git-pull 的输出,为什么它会崩溃,让 repo 处于这种状态?

【问题讨论】:

    标签: git bash shell git-pull


    【解决方案1】:

    首先,这里最大的问题是一个 shell 问题,它与 git pull 本身没什么关系。让我们做一些琐碎的事情:

    $ echo foo | read msg; echo $msg
    
    $ 
    

    为什么$msg 这里是空的?答案与管道、解析和子外壳有关。


    shell 命令的基本语法是由一系列分号和/或换行符分隔的一系列“管道”。也就是说,给定:

    a | b; c
    

    这与以下解析非常相似:

    (a | b); c
    

    或完全一样:

    a | b
    c
    

    具体来说,b 部分绑定到 a 部分,c 部分稍后出现。当然,添加显式括号会导致使用子shell,因此您可能会本能地意识到,如果b 部分是read 命令,则c 部分将没有可用的变量,因为设置只影响子外壳,不影响外壳。

    唉,删除括号没有帮助。确实,这在主 shell 中运行 a 部分,但为了读取管道的输出,b 部分仍然在子 shell 中运行。 (事实上​​,如果没有一些棘手的优化,当您使用括号时,b 部分会在子子 shell 中运行:显式调用的子 shell 的子 shell。)

    这就是为什么在 piped-to 部分退出后无法访问 $msg 的原因:任何管道的右侧总是在子 shell 中运行。


    一切都没有完全消失:考虑一下:

    $ echo foo | { read msg; echo $msg; }
    foo
    $ 
    

    这里的技巧是在子shell 中使用$msg 运行整个序列。 (括号也可以,语法稍微不那么笨拙: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
    

    虽然确切的行为取决于很多东西。 (例如,特别是“rewinding”消息本身表明您已将 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 的工作原理是暂时离开分支,在新的匿名(未命名的“非分支”)分支上累积新提交,然后移动原始分支标签,以便现在命名新的匿名分支,并且放弃了先前命名的分支。添加--quiet 会停止所有输出,以便read msg 等待整个git pull 序列完成(之后read 失败,因为毕竟没有输出)。


    1这个真的应该用mktemp;以上仅供参考。

    【讨论】:

    • 非常好,表明我没有在那里测试read 的行为。使用 while 循环效果很好。我选择这个的原因是我将命令输出传递给一个函数(我没有测试)。
    【解决方案2】:

    注意:Git 2.2(2014 年 11 月)的一项新功能可能会对这个问题产生影响,并避免任何信号 13 问题(可能不完全符合您的情况,但更普遍)。

    Patrick Reynolds (piki) 看到commit 7559a1b

    取消阻止并取消忽略SIGPIPE

    被阻止和被忽略的信号——但未被捕获的信号——在exec中继承。
    一些信号处理行为草率的调用者可以调用 git,而 SIGPIPE 被阻止或忽略,甚至是不确定的。
    当 SIGPIPE 被阻止或忽略时,几个 git 命令可以无限期地运行,忽略从 write() 调用返回的 EPIPE,即使调用它们的进程已经消失。
    我们的具体案例涉及将git diff-tree 输出到读取有限差异数据的脚本的管道。

    在理想情况下,git 永远不会被 SIGPIPE 阻止或忽略。
    但在现实世界中,包括 Perl、Apache 和 Unicorn 在内的几个真正的潜在调用者有时会生成忽略 SIGPIPE 的子进程。

    强化 git 以防止这个错误比在每个潜在的父进程中清理它更容易且更有效率

    将 SIGPIPE 的处理方式恢复为默认值,这是我们所期望的

    【讨论】:

      猜你喜欢
      • 2015-07-02
      • 1970-01-01
      • 2013-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-30
      • 2020-10-14
      • 1970-01-01
      相关资源
      最近更新 更多