如何使用我偏好的diff工具/查看器查看“git diff”输出?

824

当我键入git diff命令时,我希望使用我选择的可视化差异工具(Windows上的SourceGear "diffmerge")查看输出。如何配置git以实现这一点?


118
在所有新版本的git中,您可以使用“git difftool”代替“git diff”,这将打开一个可视化的差异比较程序。 - Debajit
2
gitx - http://gitx.frim.nl/. 就这样了。 - Alex Grande
1
在这里你可以找到简短的教程:http://nathanhoad.net/how-to-meld-for-git-diffs-in-ubuntu-hardy - Dmitry Bespalov
使用git difftool -tool=opendiff命令并将opendiff替换为任何其他工具。 - potench
6
在 Git Bash 中前往您的代码库。输入 git config diff.tool winmerge。通过输入 git difftool 来验证它是否可用。通过输入 git config --global difftool.prompt false 来取消提示。我建议使用 p4merge 而不是 winmerge。 - Michael S.
显示剩余3条评论
26个回答

414
自 Git1.6.3 起,您可以使用 git difftool 脚本:请参见 下面我的回答

也许这篇文章可以帮到你。以下是最好的部分:

有两种不同的方法来指定外部差异工具。

第一种方法是你使用的方法,通过设置GIT_EXTERNAL_DIFF变量。然而,变量应该指向可执行文件的完整路径。此外,由GIT_EXTERNAL_DIFF指定的可执行文件将被调用并带有一组固定的7个参数:

path old-file old-hex old-mode new-file new-hex new-mode

作为大多数差异工具将需要不同的参数顺序(只有一些)的情况,您很可能需要指定一个包装脚本,该脚本再调用真正的diff工具。

第二种方法,我更喜欢的是通过"git config"配置外部diff工具。这是我所做的:

1)创建一个包装脚本"git-diff-wrapper.sh",其中包含类似以下内容:

-->8-(snip)--
#!/bin/sh

# diff is called by git with 7 parameters:
# path old-file old-hex old-mode new-file new-hex new-mode

"<path_to_diff_executable>" "$2" "$5" | cat
--8<-(snap)--

正如您所见,仅第二个(“old-file”)和第五个(“new-file”)参数将传递给diff工具。

2)类型

$ git config --global diff.external <path_to_wrapper_script>

在命令提示符处,将“<path/to/git-diff-wrapper.sh>”替换为路径,以便您的~/.gitconfig包含以下内容:
-->8-(snip)--
[diff]
    external = <path_to_wrapper_script>
--8<-(snap)--

请确保使用正确的语法来指定包装脚本和差异工具的路径,即使用正斜杠而不是反斜杠。在我的情况下,我有

[diff]
    external = \"c:/Documents and Settings/sschuber/git-diff-wrapper.sh\"

在 .gitconfig 中

"d:/Program Files/Beyond Compare 3/BCompare.exe" "$2" "$5" | cat

在包装脚本中。注意末尾的"cat"!

(我想 '| cat' 仅对某些可能不返回正确或一致返回状态的程序是必需的。如果您的差异工具具有显式返回状态,则可以尝试省略结尾的 cat)

(Diomidis Spinellis 在评论中补充道:

需要 cat 命令,因为diff(1)默认退出并显示错误代码,如果文件不同。
Git希望外部差异程序只有在出现实际错误时才退出并显示错误代码,例如如果它运行内存不足。
通过将git的输出传输到cat,可以掩盖非零错误代码。
更有效的方法是,程序只能运行 exit 并带有一个参数为 0。)


上述文章引用的是通过配置文件定义外部工具的理论(而不是通过环境变量)。

在实践中(仍然针对配置文件定义的外部工具),您可以参考以下内容:


2
啊,我已经设置了外部差异程序,但不知道有7个参数,谢谢。 - user3891
5
谢谢夸奖,这非常方便。我使用了“opendiff”来设置它(在Mac OS X中启动了流畅的XCode FileMerge工具)。 - Ryan Delucchi
1
@Ryan:太好了 :) 你是用这个答案中详细说明的 "diff.external" 设置还是我下面第二个回答中的 "git difftool"? - VonC
1
太棒了!谢谢,我尝试了DiffMerge和opendiff两种工具,都运行得非常好。 - vfilby
5
所以git向diff程序发送7个参数?我越了解Git,就越觉得它是为一个人制作的:原始程序员。这个分布式版本控制系统似乎更像一个不做太多事情的闪亮玩具。 - C.J.
显示剩余11条评论

227

为了补充我之前的"diff.external" config answer

正如Jakub所提到的, Git1.6.3引入了git difftool, 最初在2008年9月提出:

用法='[--tool=tool] [--commit=ref] [--start=ref --end=ref] [--no-prompt] [file to merge]'
(请参阅此答案最后部分的--extcmd)

$LOCAL包含起始修订版文件的内容,$REMOTE包含结束修订版文件的内容。
$BASE包含工作树中文件的内容。

它基本上是修改后的git-mergetool,用于操作git索引/工作树。

这个脚本的常见用例是当您有已暂存或未暂存的更改,并且希望在并排的差异查看器(例如xxdifftkdiff等)中查看更改时。

git difftool [<filename>*]

另一个用例是当您想查看相同信息但比较任意提交时(这是revarg解析可能更好的部分)。
git difftool --start=HEAD^ --end=HEAD [-- <filename>*]

最后一个使用案例是当您想将当前工作树与除HEAD(例如标签)之外的其他内容进行比较时。
git difftool --commit=v1.0.0 [-- <filename>*]

注意:自Git 2.5以来,git config diff.tool winmerge就足够了!
请参见 "git mergetool winmerge"

而且,自Git 1.7.11开始,您可以使用选项--dir-diff,以便在填充两个临时目录后,生成可以比较两个目录层次结构的外部diff工具,而不是针对每个文件对运行外部工具实例。


Git 2.5之前:

使用自定义差异工具配置difftool的实际案例:

C:\myGitRepo>git config --global diff.tool winmerge
C:\myGitRepo>git config --global difftool.winmerge.cmd "winmerge.sh \"$LOCAL\" \"$REMOTE\""
C:\myGitRepo>git config --global difftool.prompt false

如果将winmerge.sh存储在您的PATH目录的一部分中:

#!/bin/sh
echo Launching WinMergeU.exe: $1 $2
"C:/Program Files/WinMerge/WinMergeU.exe" -u -e "$1" "$2" -dl "Local" -dr "Remote"

如果您有其他工具(kdiff3,P4Diff等),请创建另一个shell脚本,并使用适当的"difftool.myDiffTool.cmd"配置指令。
然后,您可以轻松地使用"diff.tool"配置切换工具。
您还可以添加Dave的博客文章以获取其他详细信息。
(或这个问题用于winmergeu选项)
这个设置的好处是winmerge.sh脚本:您可以根据特殊情况自定义它。
例如,请参见David Marble下面的答案,其中包括以下内容:
- 任一原始或目标中的新文件 - 任一原始或目标中的已删除文件

Kem Mason他的回答中提到,您还可以使用--extcmd选项避免使用任何包装器

--extcmd=<command>

指定自定义命令以查看差异。当指定此选项时,git-difftool 忽略配置的默认值并运行$command $LOCAL $REMOTE

例如,这就是gitk 能够运行/使用任何 diff 工具的方式。


11
为了避免"$LOCAL"和"$REMOTE"变为空字符串,我不得不转义"$"。我会将其编辑为以下内容:git config --global difftool.winmerge.cmd "winmerge.sh \"\$LOCAL\" \"\$REMOTE\"" - Carlos Rendon
1
@Carlos:Frank(请参见上面的评论)希望您查看https://dev59.com/YHM_5IYBdhLWcg3wzmkw#1291578(用于在一个WinMerge窗口中打开多个差异)。 - VonC
1
在v1.7.11中,--start=--end=选项无法识别。 - Michael
@VonC,选项-ub是干什么用的?我在WinMerge的命令行选项中没有看到。 - legends2k
1
@SteveChambers 是的,我在 https://dev59.com/r2855IYBdhLWcg3wm1m3#10879804 中提到了它。我会编辑这个答案。 - VonC
显示剩余5条评论

121

试试这个解决方案:

$ meld my_project_using_git

Meld可以理解Git,并提供浏览最近更改的功能。


19
еӨӘеҘҪдәҶпјҢй”®е…Ҙmeld .жҜ”й”®е…Ҙgit difftool并жҢүйЎәеәҸжҹҘзңӢжӣҙж”№зҡ„ж–Ү件иҰҒе®№жҳ“еҫ—еӨҡгҖӮиҝҷжӣҙжҺҘиҝ‘дәҺMercurialзҡ„hg vdiffжҸ’件гҖӮ - Tom
7
此外,meld . 在 Mercurial 和 Subversion 项目中也适用,如果有人感兴趣的话。 - Tom
唯一的缺点是:有时候使用meld扫描差异比使用difftool要花费更长的时间。看起来它仍然会扫描gitignore文件(在我的情况下有很多生成的构建文件)。在我的机器上,创建修改文件列表需要几秒钟的时间,但使用difftool则可以立即完成。 - Vadim Kotov
太棒了的提示!对我非常有用! - webasdf
2
如果您想查看当前更改,那就太好了。我发现 git difftool -y -t meld --dir-diff other_branch 也很有用,可以查看与任何分支的不同之处。 - Eric Duminil
显示剩余4条评论

52

1
什么是$local和$remote? - C.J.
3
如果有不需要包装脚本的方法,返回翻译后的文本:+1 - jhewlett
@CJohnson:分别是原始文件路径和编辑后的文件路径。 - jhewlett
如果博客链接失效,这里是相关的SO答案链接:https://dev59.com/YHM_5IYBdhLWcg3wzmkw#1291578 - Tobias Kienzler
1
@CJohnson:$LOCAL始终是最新的提交。但是$REMOTE不一致。当查看本地沙盒中的更改时,它是当前/编辑的文件,但是在比较历史提交时,它是_parent_提交。换句话说,对于普通的差异,$LOCAL=旧的,$REMOTE=新的。对于历史记录,$LOCAL=更新的,$REMOTE=旧的。 - Granger

45
自 Git 版本 1.6.3 起,有 "git difftool" 命令可用,您可以配置使用您最喜欢的图形化 diff 工具。目前支持(撰写本回答时)的开箱即用工具有 KDiff3, Kompare, tkdiff, Meld, xxdiff, emerge, vimdiff, gvimdiff, ecmerge, Diffuseopendiff;如果您想要使用的工具不在此列表中,您总是可以使用 'difftool.<tool>.cmd' 配置选项。"git difftool" 命令接受与 "git diff" 相同的选项。

1
Araxis Merge也已经配置完成。http://www.araxis.com/merge/scm_integration.html - ΩmegaMan
12
示例用法:git difftool -t kdiff3 HEAD - emanaton

28

我有一个补充。我喜欢定期使用一些不被默认工具支持的 diff 应用程序(例如 kaleidoscope),通过

git difftool -t

我还希望默认的diff命令行工具保持原样,所以设置GIT_EXTERNAL_DIFF变量不是一个选项。

你可以使用以下命令临时使用任意的diff应用程序:

git difftool --extcmd=/usr/bin/ksdiff

它只是将您指定的命令传递给这两个文件,因此您可能不需要包装器。


2
好的观点:还没有人提到“--extcmd”选项。+1。我已经在我的答案中包含了它。 - VonC
你能给“kaleidoscope”添加一个引用吗? - Peter Mortensen

24

VonC的答案的基础上,针对文件的移除和添加,请使用以下命令和脚本:

git config --global diff.tool winmerge
git config --global difftool.winmerge.cmd "winmerge.sh \"$LOCAL\" \"$REMOTE\" \"$BASE\""
git config --global difftool.prompt false

这相当于将以下内容放入您的全局文件.gitconfig中:

[diff]
    tool = winmerge
[difftool "winmerge"]
    cmd = winmerge.bat "$LOCAL" "$REMOTE" "$BASE"
[difftool]
    prompt = false

然后将以下内容放入文件winmerge.sh中,此文件必须在您的路径上:

#!/bin/sh
NULL="/dev/null"
if [ "$2" = "$NULL" ] ; then
    echo "removed: $3"
elif [ "$1" = "$NULL" ] ; then
    echo "added: $3"
else
    echo "changed: $3"
    "C:/Program Files (x86)/WinMerge/WinMergeU.exe" -e -ub -dl "Base" -dr "Mine" "$1" "$2"
fi

winmerge.sh脚本的绝佳补充。+1。我已经更新了我的回答并链接到了你的回答。 - VonC
1
winmerge.bat in the second snippet should be winmerge.sh - BartoszKP
你能为许多工具制作这个吗?并通过文件扩展名将它们关联起来吗? - yucer

12

针对Windows/MSYS Git的解决方案

在阅读了答案之后,我发现有一种更简单的方法,只需要更改一个文件。

  1. 创建一个批处理文件来调用您的diff程序,并传递参数2和5。这个文件必须放在您的路径中的某个地方。(如果你不知道在哪里,放在C:\windows里。)例如,称之为“gitdiff.bat”。我的是:

    @echo off
    REM This is gitdiff.bat
    "C:\Program Files\WinMerge\WinMergeU.exe" %2 %5
    
    设置环境变量指向你的批处理文件。例如:GIT_EXTERNAL_DIFF=gitdiff.bat。或通过 PowerShell 输入 git config --global diff.external gitdiff.bat

    重要的是不要使用引号或指定任何路径信息,否则它将无法工作。这就是为什么gitdiff.bat必须在您的路径中。

    现在当你输入“git diff”时,它会调用你的外部差异查看器。

1
+1(如果可以的话,+10)。这真的是最简单的解决方案。我花了几个小时来尝试接受的答案,最终放弃了(我的一些问题可能与通过PowerShell运行Git有关...)。然而,我使用了git config --global diff.external winmerge.cmd,而不是设置GIT_EXTERNAL_DIFF环境变量,它同样有效。 - Christoffer Lette
这个解决方案有几个问题:
  1. 对于新文件无效。WinMerge要求我输入第二个文件进行比较。
  2. 只有在关闭当前的WinMerge窗口后,才会打开下一个文件的比较窗口,没有简单的方法同时查看所有文件。
- Jesus H

10
如果你是通过 Cygwin 进行此操作,你可能需要使用 cygpath:
$ git config difftool.bc3.cmd "git-diff-bcomp-wrapper.sh \$LOCAL \$REMOTE"
$ cat git-diff-bcomp-wrapper.sh
#!/bin/sh
"c:/Program Files (x86)/Beyond Compare 3/BComp.exe" `cygpath -w $1` `cygpath -w $2`

由于某些原因,我不能再使用“bc3”了,但如果我改用“beyond”,就可以正常工作。 - idbrii

9

在查看了其他一些外部的差异工具之后,我发现IntelliJ IDEA(和Android Studio)中的diff视图最适合我。

第一步 - 设置从命令行运行IntelliJ IDEA

如果您想使用IntelliJ IDEA作为差异工具,您应该首先按照这里的说明设置从命令行运行IntelliJ IDEA:

在macOS或UNIX上:

  1. 确保IntelliJ IDEA正在运行。
  2. 从主菜单中选择Tools | Create Command-line Launcher。打开Create Launcher Script对话框,并提供了默认的路径和启动器脚本的名称建议。您可以接受默认值,也可以指定自己的路径。 记住它,因为稍后会用到。
  3. 在IntelliJ IDEA以外的地方,将启动器脚本的路径和名称添加到您的路径中。

在Windows上:

  1. 在Path系统环境变量中指定IntelliJ IDEA可执行文件的位置。在这种情况下,您将能够从任何目录调用IntelliJ IDEA可执行文件和其他IntelliJ IDEA命令。

第二步 - 配置git使用IntelliJ IDEA作为差异工具

按照这篇博客文章中的说明:

Bash

export INTELLIJ_HOME /Applications/IntelliJ\ IDEA\ CE.app/Contents/MacOS
PATH=$IDEA_HOME $PATH

set INTELLIJ_HOME /Applications/IntelliJ\ IDEA\ CE.app/Contents/MacOS
set PATH $INTELLIJ_HOME $PATH

现在将以下内容添加到您的git配置文件中:
[merge]
   tool = intellij
[mergetool "intellij"]
   cmd = idea merge $(cd $(dirname "$LOCAL") && pwd)/$(basename "$LOCAL") $(cd $(dirname "$REMOTE") && pwd)/$(basename "$REMOTE") $(cd $(dirname "$BASE") && pwd)/$(basename "$BASE") $(cd $(dirname "$MERGED") && pwd)/$(basename "$MERGED")
   trustExitCode = true
[diff]
   tool = intellij
[difftool "intellij"]
   cmd = idea diff $(cd $(dirname "$LOCAL") && pwd)/$(basename "$LOCAL") $(cd $(dirname "$REMOTE") && pwd)/$(basename "$REMOTE")

您可以使用git difftoolgit difftool HEAD~1来尝试它。

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