Git 预推挂钩在管道传输到 xclip 后挂起

Git pre-push hook hangs after piping to xclip

序言:

提交时,我倾向于将消息格式化如下:

[<task number>] <task title>

应通过预推钩将其转换为有效的 GitHub 语法,如下所示:

Work item [[<git_branch>](http://tracker/_workitems/<git_branch>)]:

- [x] [[<task_number>](http://tracker/_workitems/<task_number>)] <task title>

然后它被 cat 输出到控制台输出,这样我就可以将它复制粘贴到 github 上的 PR 描述中。

任务

更进一步,将该消息放入剪贴板,这样我就不必手动select它并从控制台复制它。 因为我在 Linux 机器上,所以我决定使用 xclip 来完成任务。

我当前的 git 挂钩脚本如下所示:

#!/bin/sh

PBI=\`git symbolic-ref --short HEAD\`

echo "**Backlog Item [$PBI]:**\n" > pr_messages/$PBI.md

git log develop..HEAD --format=" - [x] %B" >> pr_messages/$PBI.md

sed -r -i 's|\[([0-9]{4,})\]|[[](http://tracker/_workitems/)]|g' pr_messages/$PBI.md

cat pr_messages/$PBI.md

问题

当我将以下行添加到此脚本的末尾时

cat pr_messages/$PBI.md | xclip -selection clipboard

我在我的 Ctrl+C/V 剪贴板中收到消息,但 git 挂起,我必须中止它。鉴于它应该是一个预推送挂钩,它有效地阻止了我实际推送我的代码。

UPD: 正如 @wumpus-q-wumbley 所提议的,这里是 strace 输出:

$> ps aux | grep git

kraplax  29796  0.0  0.0  25696  5660 pts/1    S+   12:55   0:00 git push
kraplax  29797  0.0  0.0  48276  3040 pts/1    S+   12:55   0:00 ssh git@github.com git-receive-pack 'eduard-sukharev/ProFIT.git'

$> sudo strace -p 29796
Process 29796 attached
wait4(29797, 
^CProcess 29796 detached
 <detached ...>
$> sudo strace -p 29797
Process 29797 attached
select(7, [3 4], [], NULL, NULL
^CProcess 29797 detached
 <detached ...>

这基本上表明 git-push 正在等待挂起的 ssh 进程 ssh git@github.com git-receive-pack 'eduard-sukharev/ProFIT.git'。这一切都稍微转移了问题的焦点。

UPD2: 设置 GIT_TRACE=~/git_trace.log 给出了这个信息:

$ cat ../git_trace.log 
trace: built-in: git 'rev-parse' '--abbrev-ref' 'HEAD'
trace: built-in: git 'rev-parse' '--abbrev-ref' 'HEAD'
trace: built-in: git 'status' '--porcelain'
trace: built-in: git 'push'
trace: run_command: 'ssh' 'git@github.com' 'git-receive-pack '\''eduard-sukharev/ProFIT.git'\'''
trace: run_command: '.git/hooks/pre-push' 'origin' 'git@github.com:eduard-sukharev/ProFIT.git'
trace: built-in: git 'symbolic-ref' '--short' 'HEAD'
trace: built-in: git 'log' 'develop..HEAD' '--format= - [x] %B'
trace: built-in: git 'rev-parse' '--abbrev-ref' 'HEAD'
trace: built-in: git 'status' '--porcelain'

问题

如果没有,为什么这个进程会挂起?

我应该如何重写该行以完成预期任务?

我是否应该使用 xclip 以外的其他工具来管理剪贴板?

我怀疑 xclip 后台进程在脚本的原始标准输出上仍然有一个打开的文件描述符,阻止 git 在它读取挂钩输出的管道上接收到 EOF。尝试将 > /dev/null 添加到 xclip 命令。

xsel, as well as xclip,等待另一个程序明确获取所选数据。

This behavior is a design-conditional of X11, since there "is no X selection buffer. The selection mechanism in X11 is an interclient communication mediated by the X server each time any program wishes to know the selection contents [...]. In order to implement modification of the selection(s) (in input, keep and exchange modes) [these programs detach] from the terminal, spawning a child process to supply the new selection(s) on demand. This child exits immediately when any other program takes over the selection(s)" -- from the xsel man-page

换句话说,您的 Gits pre-push 提交会一直执行,直到您开始向剪贴板提供文本选择。然后该过程将停止,直到您通过调用任何命令或执行任何 "fetches" 剪贴板文本的程序来使用此文本片段。

分叉 xclip-命令不起作用

我的第一个想法是分离这个提供选择的过程,让它与(当时)正在进行的钩子脚本执行并行。不幸的是,这不起作用,要么主进程停止,直到子进程 returns,或者分叉命令的文本选择对当前 X11 服务器不可用。

可能的解决方案

由于 X11 中 "clipboard" 的行为,您必须避免在 git-hooks 的时间相关处理中提供文本选择。

  1. 使用剪贴板管理器 - 大多数剪贴板管理器(如 Klipper [for KDE] or Glipper [for Gnome])提供了解耦供应的机制来自其用法的数据 - 模拟 Windows 和 Mac OS.

  2. 的剪贴板行为
  3. 别名 git-push-command - 如果您主要在 [=65= 中操作 git-存储库] 您可以将 git-push-command 包装在别名或小的 shell-script 中,它首先调用推送,然后将文本片段提供到剪贴板。这种方法的缺点是,(除了 cli 依赖性之外)该命令将 "hang" 最后,直到您获取剪贴板内容。

除非您无法在系统中安装其他软件,否则我建议您使用剪贴板管理器。

使用 Klipper 将 CLI 中的文本放入 X11 剪贴板

您可以使用 qdbus - a command-line-interface to Qt-applications. An example took from Milian Wolff's Blog Post 通过 shell 访问 Klipper,根据您的脚本进行调整,可能如下所示:

#!/bin/sh

PBI=\`git symbolic-ref --short HEAD\`
echo "**Backlog Item [$PBI]:**\n" > pr_messages/$PBI.md
git log develop..HEAD --format=" - [x] %B" >> pr_messages/$PBI.md

sed -r -i 's|\[([0-9]{4,})\]|[[](http://tracker/_workitems/)]|g' pr_messages/$PBI.md

PR_MESSAGE=$(cat pr_messages/$PBI.md)
qdbus org.kde.klipper /klipper setClipboardContents "$PR_MESSAGE"

关于qdbus和Klipper交互的另一篇有趣的文章:https://askubuntu.com/questions/393601/a-command-for-pasting-the-same-as-ctrl-v