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

标签 git githooks xclip

序言:

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

[<task number>] <task title>

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

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

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

然后是cat ted 到控制台输出,以便我可以将其复制粘贴到 github 上的 PR 描述。

任务

更进一步,将该消息放入剪贴板,这样我就不必手动选择它并从控制台复制。 由于我使用的是 Linux 计算机,因此我决定使用 xclip来完成任务。

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

#!/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,})\]|[[\1](http://tracker/_workitems/\1)]|g' pr_messages/$PBI.md

cat pr_messages/$PBI.md

问题

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

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

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

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 <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="44232d3004232d302c31266a272b29" rel="noreferrer noopener nofollow">[email protected]</a> 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 <a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="0e69677a4e69677a667b6c206d6163" rel="noreferrer noopener nofollow">[email protected]</a> 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' '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="83e4eaf7c3e4eaf7ebf6e1ade0ecee" rel="noreferrer noopener nofollow">[email protected]</a>' 'git-receive-pack '\''eduard-sukharev/ProFIT.git'\'''
trace: run_command: '.git/hooks/pre-push' 'origin' '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="65020c1125020c110d10074b060a08" rel="noreferrer noopener nofollow">[email protected]</a>: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 之外,我是否应该使用其他工具来管理剪贴板?

最佳答案

xsel ,以及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 提交将一直执行,直到您开始向剪贴板提供文本选择。然后,该过程将停止,直到您通过调用任何命令或执行任何“获取”剪贴板文本的程序来使用此文本片段。

fork xclip - 命令不起作用

我的第一个想法是分离这个提供选择的过程,让它与(当时)正在进行的钩子(Hook)脚本执行并行。不幸的是,这不起作用,要么主进程无论如何都会停止,直到子进程返回,要么当前的 X11 服务器无法使用 fork 命令的文本选择。

可能的解决方案

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

  1. 使用剪贴板管理器 - 大多数剪贴板管理器(如 Klipper [对于 KDE] 或 Glipper [对于 Gnome])提供了解耦数据供应的机制从它的用法来看 - 它模拟 Windows 和 Mac OS 的剪贴板行为。

  2. 别名 git-push-command - 如果您主要在 shell 中操作 git-repository,则可以将 git- 包装起来Push - 别名或小型 shell 脚本中的命令,首先调用推送,然后将文本片段提供到剪贴板。这种方法的缺点是(除了 cli 依赖性之外)该命令将在最后“挂起”,直到您获取剪贴板内容。

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

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

您可以使用 qdbus 通过 shell 访问 Klipper - Qt 应用程序的命令行界面。一个例子来自 Milian Wolff's Blog Post ,根据您的脚本进行调整,可能如下所示:

#!/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,})\]|[[\1](http://tracker/_workitems/\1)]|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

关于Git 预推送 Hook 在管道传输到 xclip 后挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31855090/

相关文章:

windows - 将 Git 分支添加到命令提示符

git - Jenkins 管道私有(private)存储库依赖

git - 如何正确使用带有 Git 钩子(Hook)的 CI 脚本来压缩源代码

ruby - 为什么 `xclip .bashrc` 在 ruby​​ 中比系统 ("xclip .bashrc"花费的时间长得多?

ssh - xclip 在 Ubuntu VirtualBox VM 的 tmux session 中提供 `Error: Can' t 打开显示 : localhost:10. 0`

git - 如何优化我的gitlab-ci.yml任何想法?

git - 获取 Git 存储库的最后一次提交

python - 远程 : ImportError: No module named gitlab

bash - 使用 xclip 将剪贴板内容粘贴到 bash 中的变量中

bash - 如何从 git 本身获取 git 的 `.git` 路径?