从ruby运行 `git add -p`

3

我正在尝试从Ruby运行git add -p命令。问题在于此命令会显示文件的部分内容并等待用户输入,可能会打开git编辑器。由于这个原因,常规的内核方法执行系统命令将无法工作。我尝试使用open3,目前的情况如下:

require "open3"
Open3.popen3("\git add -p #{files_to_add.join(" ")}") do |stdin, stdout, stderr, wait_thr|
end

我不知道应该在代码块里放什么内容,也没有在互联网上找到任何线索。

有什么想法可以解决这个问题吗?

注意:我不想使用任何宝石(gems)

编辑:open3无法工作。我现在正在尝试使用pty。以下是我的代码:

require "pty"
begin
  PTY.spawn("\git add -p #{files_to_add.join(" ")}") do |r, w, pid|
    begin
      r.each { |line| print line }
    rescue Errno::EIO
    end
  end
rescue PTY::ChildExited => e
  puts "The child process exited!"
end

这段代码打印了第一个要打补丁的块,但是接下来"问题"(换句话说,标准输入提示)并没有出现。显然,我没有在上面的代码中打印它,但我似乎找不到如何解决这个问题。有什么想法吗?


2
你确定要使用启用交互模式的 git add -p 命令吗?看起来你正在自动化/简化某些工作流程。也许有一些 git 命令和选项可以避免交互模式。我不知道你脚本的上下文,但是深入研究一下 git 手册可能会很值得。 - zwippie
是的,我确定我想使用 git add -p,因为我正在为我的个人项目创建该命令的包装器。我目前正在尝试使用 pty,看起来我已经有所进展了。 - Robert Audi
如果您生成一个新的终端,我认为在该块上下文中打印将适用于它:您实际上是在打印到一个未显示的终端。我怀疑第一种方法更有可能成功。我对Open3不太熟悉,无法给出答案,但我猜想您需要根据需要写入stdin,可能需要使用gets或类似的复杂操作来收集输入。另外,有一个git gem可以管理这种东西。因此,也许可以查看其代码以获取答案? - Denis de Bernardy
你为什么想要使用一个 gem? - Малъ Скрылевъ
你想用你的包装器解决什么样的问题?我只是好奇,因为我真的想不出一个问题,我会通过显式运行命令的交互版本来完全自动化交互。 - michas
我不确定,但为什么你不能只使用反引号或%x {command}?我在Windows上尝试了一下pause命令,它等待输入。 - Darek Nędza
1个回答

3

pty标准库模块中(不需要任何gems),有一个内部模块可以require,名为expect。它将在IO上添加一个expect方法。

你可能需要像这样的东西:

require 'pty'
require 'expect'

PTY.spawn "git add -p" do |r, w, pid|
  w.sync = true
  r.expect ']? ' do |got|
    puts got
    puts 'responding with q'
    w.write "q\r"
    puts r.expect "\n", 9
  end
end

1
那个运行得非常出色。但我有几个问题:1. puts got 可以工作,但 print got 不行。为什么?2. 您的代码只涵盖了一个迭代(或块)。我应该循环遍历哪些块来完成所有操作?3. 如果将 q 替换为 y,则应该已经暂存了一个块,但实际上没有。为什么? - Robert Audi
1
  1. IO#expect 返回一个数组,比 Kernel#print 更优雅地处理了 IO#puts
  2. 我猜你应该无条件循环并调用 expect(s,5)(5 秒超时),直到最终遇到超时才退出,除非你可以使用 expect 检测结束。
  3. 我猜你需要在 y 后面再加一个 expect,否则退出可能会在 git 进程读取和响应你输入的内容之前结束。没有它,即使通常情况下可以工作,也肯定存在竞争条件。
- DigitalRoss
1
顺便提一下,您不必使用带有“#expect”的块...您可以直接无条件调用w.write,这在您的情况下可能是有意义的。请务必考虑使用“#expect”的可选超时参数。 - DigitalRoss
我差不多把它按照我的想法做好了!但还有一个问题:现在w.write "...\r实际上会将...打印到屏幕上。这很烦人,因为在我的情况下,...是用户输入的内容。有没有一种方法可以“写入”但不“显示”(类似于“发送”方法或其他方法)? - Robert Audi
我想这是因为你正在打印从#expect返回的内容。 - DigitalRoss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接