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 的时间相关处理中提供文本选择。
使用剪贴板管理器 - 大多数剪贴板管理器(如 Klipper [for KDE] or Glipper [for Gnome])提供了解耦供应的机制来自其用法的数据 - 模拟 Windows 和 Mac OS.
的剪贴板行为
别名 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
序言:
提交时,我倾向于将消息格式化如下:
[<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 的时间相关处理中提供文本选择。
使用剪贴板管理器 - 大多数剪贴板管理器(如 Klipper [for KDE] or Glipper [for Gnome])提供了解耦供应的机制来自其用法的数据 - 模拟 Windows 和 Mac OS.
的剪贴板行为
别名
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