如何使用分页器对较长的git add --patch hunks进行操作?

15

当我使用 git add --patch 交互式地添加diff补丁时,有些补丁的长度超过了屏幕,但是无法使用less来浏览这些补丁,这令我感到奇怪。

因为我已经设置了:

[core]
      pager = less -FRX --tabs=4

[pager]

  diff = diff-highlight | less -FRX --tabs=4

interactive.diffFilter=通过less管道传递也无法帮助分页。

我需要做什么才能让git add--patch使用less,以便我可以使用键盘浏览任何超过一页的输出?


git版本2.10.0.GIT - Tom Hale
git版本为2.28.0 -- 问题仍然存在。 - Tom Hale
@VonC 有什么想法吗? - Tom Hale
3个回答

6

你做不到。这是Unix管道模型的本质。

使用分页器的Git命令之所以可以与任何通用的stdin分页器一起使用,是因为这个模型。这些git命令将它们的输出写入stdout。分页器从stdin读取输入。前者的stdout被管道传输到后者的stdin。正是这个管道模型的简单性使得分页器通用,并且允许git让你选择自己的分页器,只要它使用这个模型。

那么为什么git add -p(或git add -i)不能像git diffgit log一样呢?

因为一次只能有一个交互式应用程序。

git diffgit log不是交互式的。 git add -p和分页器是交互式的。

管道模型的本质意味着一次只能有一个应用程序处于控制状态,并且需要一个交互式应用程序来控制。为了让分页器控制终端(以便它可以显示提示并响应您的输入),git add -p必须释放控制权。一旦它这样做,就无法重新获得控制权。

这样看待它:将会有两个命令行提示符试图与您进行交互:一个是git add -p的提示符,另一个是分页器的提示符。他们如何协调?它将不得不像这样进行:

  1. git add -p会将一个补丁与一个结束补丁(EOH)标记一起写入stdout,而不是通常的文件结束(EOF)标记。
  2. git add -p然后将控制权移交给管道另一端的任何应用程序。
  3. 分页器将接收到补丁,并控制终端以及显示补丁的各个部分以及其命令提示符。
  4. 分页器会像平常一样运行,但有一个重大区别。通常它会看到EOF标记,当您说完成(quit命令)时,它就会退出。但EOH maker告诉分页器:“不要退出。当用户完成时,将控制权交回给上游应用程序。不要退出。等待。”
  5. 因此,当您使用各种分页器命令查看补丁后,您将使用其quit命令告诉它您已完成,就像往常一样。
  6. 但现在,分页器不会退出,而是以某种方式将终端控制权交回给git add
  7. git add的终端提示符将替换分页器的终端提示符...
  8. ...现在我们回到第1步。不断重复,直到EOF。

正如您所看到的,这不仅是一种糟糕的用户体验(使用分页器的quit命令在每个块上返回git add),它还会完全 削弱 破坏 Unix 管道模型的力量和美感

出于同样的原因,git add -p 不能使用 diff-so-fancy

git -p 拥有分页器般的行为的唯一方法是内置一个,或者定义一个“Git 分页器 API”,然后我们等待人们编写与此 API 兼容的分页器。这是插件模型,与管道模型非常不同。它还意味着紧密集成:需要将 git add -p 命令和分页器命令组合并在每个命令提示符下提供。

使用终端应用程序的分页功能

我发现在我的终端窗口中向上滚动很容易。我的终端有键盘命令,可以让我逐行或逐页移动。

使用git add -psplit命令

你考虑过使用git add -psplit命令来分割补丁吗?我觉得更小的补丁更容易理解!

你的回答似乎假设两个应用程序需要同时从终端读取。我不认为这是必要的:为什么不能让 git 运行 <generate-diff> | less --quit-if-one-screen 来显示差异,等待 less 退出,然后打印补丁菜单呢? - Tom Hale
你说得完全正确。它可以。git add -p 可以在单个块上作为子进程调用分页器,而不仅仅将所有输出都管道传输给它。因此,这几乎肯定需要更改 git 的 add-patch.c。我现在没有时间去看一下。同时,尝试使用 less 自己的能力来调用子进程,看看是否可以接受这样的 UX。 - Inigo
看起来你没有理解我上面的问题。作为我的调查的一部分,我启动了在GitHub上Git存储库上的讨论,你可能是通过我的链接发现的,所以我希望你至少欣赏我的贡献,在4年的无为之后推动这个项目。即使我在第一次回答中不完全准确,但我正确地指出需要更改git add -p的工作方式,并且你不能仅仅因为我给出的原因就简单地将一个分页器附加到它上面。 - Inigo
1
你必须承认,你接受的两个答案都是hack,并且会导致糟糕的用户体验,正如我在我的回答中提到的那样。 - Inigo
你的链接不是指向git的仓库 - dsf与git是正交的,就像那里打印出来的一样。我很感激你提供了perl源代码的链接。 - Tom Hale
显示剩余4条评论

1
作为一种解决方法,您可以设置EDITOR=less并使用eedit)在大块上运行less。但是,这种解决方法存在一些缺点。可以通过类似以下内容的方式避免这些缺点:
EDITOR="EDITOR='$EDITOR' bash -c 'set -m; less -K \"\$1\"' --" git add -p
  • 在调用less之前重置EDITOR,允许在less中使用标准的v键调用编辑器。

  • less的选项-K允许使用Control-C退出less,告诉Git不要暂存差异块。使用q退出less将导致差异块被暂存。

  • set -m创建一个单独的进程组,防止Control-C向上冒泡并杀死Git进程。

还有一些工具,例如Magit,提供了更好的交互式暂存界面


1
这对我没有起作用,因为$VISUAL优先于$EDITOR使用。 - Tom Hale
1
使用 $LESS=F,如果 hunk 小于一个屏幕,则 less 会退出 0,并自动将其暂存 :( - Tom Hale

1

根据AtnNn的答案,我想出了以下别名:

ap = !"VISUAL=\"VISUAL='$VISUAL' bash -c 'set -m; less -K -+F \\\"\\$1\\\"' --\" git add -p \"$@\" #"
  • 按下eless中翻页
    • 按下q将显示的内容暂存
    • 按下v编辑当前显示的内容
    • 按下^C退出并重新打开交互式菜单

我正在为修复这个问题提交一个PR到git本身。


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