如何在文本编辑器中查看 git 差异?

How do I view a git diff in a text editor?

如何在 Atom 等纯旧文本编辑器中查看 git 差异?我不想设置或使用差异工具,我只想在任何基本文本编辑器中查看差异。

我看到的其他问题的答案是关于设置 diff 工具,但我只想指定一些随机文本编辑器。


附带问题:

如果输出很短,

git diff 将显示在您的控制台中,否则它将转到您的寻呼机。即 lessmore

git difftool 将显示在 tkdiff、xxdiff、meld、compare gvimdiff 中,...或您设置的任何内容

Difftools 通常让您在分页器处于只读状态时编辑文件

差异工具(例如:http://meldmerge.org/)将在 2、3 甚至 4 个面板(您的版本、远程版本、合并版本、祖先版本)中向您显示差异。

在文本编辑器(这是我的偏好)上打开 diff 会显示 diff 个特殊字符,表示内容的添加、删除和修改。以及一些上下文边界信息。

How do I view a git diff in a plain old text editor like Atom? I don't want to set up or use a diff tool, I just want to view the diff in any basic text editor.

Diff 输出只是文本,因此您可以将其重定向到一个文件并打开该文件,或者(取决于编辑器和平台)将输入直接通过管道传输到编辑器。

如果您做过想要运行您的编辑器作为比较工具,the documentation描述了如何调用编辑器。您可能希望包装器脚本在命令行的正确位置使用环境变量调用 atom。

Side question: What's the difference between a diff tool and any text editor?

通用文本编辑器不太可能像专用工具那样很好地显示差异。至少,如果它识别格式,它可能会突出显示特定于差异的部分。

真正的 difftool 通常会让您有机会查看文件的旧版本和新版本,并直观地显示更改所在的位置。它也可能突出线内变化(差异本身是基于线的)。


我通常反对文字图片,但由于我们真正讨论的是界面...

  1. 在文本编辑器中打开 git diff 的输出: 这与在终端中读取差异几乎没有什么不同,所以我看不出有什么好处。它确实突出显示了 index 行,但只有当我在单个 diff 中扫描多个 hunk 时,这才真正有用。

  2. GVim 逃亡插件显示相同的变化: 这是我的编辑器内部使用逃犯插件的视图。您可以看到我从 join 行中删除 space 的行内突出显示。如果我继续在右窗格中输入,它甚至会实时更新。这也是唯一一个正确语法高亮源代码的。

  3. git difftool 合并打开的相同更改 这是 IMO 更改如何适合整个文件的最友好的视图。

TL;DR: Git 本身对其他命令一无所知,所以你必须提供一组辅助指令 to Git to告诉它:要将程序 X 用作 difftool,请执行 _____。最好的办法是找到其他人已经构建的指令,如 中所示,但如果失败,那么请参见下文。

请参阅 了解一些实用技巧。

注意,就Git本身而言,尽量简单地说,diff就是比较两个修订[=104] =].例如,这两个修订可以是两个提交,或者一个提交和您的工作树内容。 Git 有一些额外的特殊情况,因此您也可以将它用作标准 Unix/Linux diff 命令的高级版本,但主要是,它是两个修订版,每个修订版都是一个整体文件集:

  • 如果左侧修订版有一个名为 F 的文件,右侧修订版有一个名为 F[=104 的文件=],Git会比较F两个版本的内容。

  • 如果左侧有一个文件 D 而它在右侧修订版中不存在,则文件 D 删除.

  • 如果左边没有文件A,但是右边有文件A-侧面修订,文件 A 添加

  • 如果启用重命名检测——这是自 Git 2.9 以来的默认设置——Git 将在添加和删除文件的情况下尝试查看是否也许新添加的文件实际上与旧的但已删除的文件大部分相同,如果是这样,请将其报告为 重命名 文件,旧名称 D(删除)和新名称 A(添加)。 Git 然后继续比较左侧 D 和右侧 A 的内容。

  • 当比较文件的内容时,如果一切都一样,Git通常只是什么都不说。否则,您可以让 Git 给您一组形式为 "remove this line" and/or "add this other line" 的指令。按照说明将文件的旧版本转换为文件的新版本。这些指示不一定是任何人实际所做的!他们只会获得相同的结果。 (通常只有一种明显的方法可以达到结果,所以结果证明这是某人所做的。)

考虑到这一点,以下是 git difftool 的作用:

  • 首先,它 Git 比较两个修订以生成有更改的文件列表,同时 抑制 实际这些文件的比较。

  • 然后,使用已更改文件的列表 - 名称集 F 在两个修订版中都存在但在两个修订版中不匹配 - 它使得文件左侧修订版的副本和右侧修订版的副本。在大多数情况下,它必须制作这些副本,因为存储在 Git 中的文件以冻结和压缩的形式保存,只有 Git 才能真正读取。 Git 必须解压缩/解冻它们,"rehydrating" 文件的内部冻干形式,以便其他程序可以读取它。

    在少数情况下,例如将特定提交与工作树进行比较时,它可能只在适当的时候使用工作树副本。它可能仍会制作自己的副本。你在这里没有任何实际的承诺。

  • 现在文件有两个副本,Git 使用帮助程序来启动您选择的差异工具。这个帮助程序被赋予了几个信息,例如:

    • 每个临时副本的名称
    • 文件的原始名称(临时副本通常有一些丑陋的临时名称,例如 .tmp-123456


    帮助程序的工作是运行 diff 工具,以便它以对用户友好的方式显示这些文件。然后它必须等待工具指示用户已完成查看文件,并查明用户是否已表示希望取消查看文件或继续下一个文件对。

This is the helper program. It is a relatively simple shell script, weighing in at just over 100 lines. But it uses more shell scripts: in particular, it uses this larger mergetool helper library,其中包含 450 多行 shell 脚本。

要提供您自己的工具,您必须——为了获得最佳使用效果——编写 shell 脚本,第二个(合并助手)库使用该脚本,并结合第一个脚本提供有关如何合并的知识运行 您选择的工具。这种互动的关键要素是:

  • 打开两个文件
  • 显示的名称比 .tmpwhatever
  • 更有用
  • 检测用户何时以及是否想要继续下一个 diff 或退出。

shell 脚本助手代码的两个现有库有一些回退,因此如果您的程序使用起来相对简单,现有的助手可以 运行 自己使用。他们只需要知道可执行文件在哪里以及要传递什么参数是否信任它的退出代码。您可以通过设置 difftool.<em>name</em>.cmdmergetool.[=56= 来使用 <code>git config 设置它们]名称.trustExitCode。例如:

git config --global difftool.foo.cmd '<path> "$LOCAL" "$REMOTE"'
git config --global mergetool.foo.trustExitCode true

如果 path 处的命令在 "go on to next diff" 中退出为零,则

可以(但不是很好:文件名将很奇怪且无用) "quit now".

非零

你还有,since Git v1.7.8 (Oct. 2011) git jump

git jump is a script for helping you jump to "interesting" parts of your project in your editor.

It works by outputting a set of interesting spots in the "quickfix" format, which editors like vim can use as a queue of places to visit (this feature is usually used to jump to errors produced by a compiler).

For example, given a diff like this:

------------------------------------
diff --git a/foo.c b/foo.c
index a655540..5a59044 100644
--- a/foo.c
+++ b/foo.c
@@ -1,3 +1,3 @@
int main(void) {
-  printf("hello word!\n");
+  printf("hello world!\n");
}
-----------------------------------

git jump will feed this to the editor:

-----------------------------------
foo.c:2: printf("hello word!\n");
-----------------------------------

不过请确保使用 Git 2.35(2022 年第一季度):git jump(在 contrib/ 中)的“merge”子命令静默忽略了 pathspec 和其他参数.

参见 commit 67ba13e (09 Nov 2021) by Jeff King (peff)
(由 Junio C Hamano -- gitster -- in commit a0f3df5 合并,2021 年 12 月 10 日)

git-jump: pass "merge" arguments to ls-files

Signed-off-by: Jeff King

We currently throw away any arguments given to git jump merge".
We should instead pass them along to ls-files, since they're likely to be pathspecs.
This matches the behavior of git jump diff, etc.