让git diff --stat显示完整的文件路径

152

在执行git diff --stat时,有些文件会列出从仓库根目录开始的完整路径,而有些文件则列出:

.../short/path/to/filename.  

路径以...开头,只显示了短路径。

我想让git diff列出所有文件的完整文件路径,以便通过脚本轻松处理。有没有办法让git diff始终显示完整路径?

8个回答

151

默认情况下,git diff 将其输出截断以适应 80 列终端。

您可以通过使用 --stat 选项 指定值来覆盖此行为:

--stat[=<width>[,<name-width>[,<count>]]]
       Generate a diffstat. You can override the default output width for
       80-column terminal by --stat=<width>. The width of the filename
       part can be controlled by giving another width to it separated by a
       comma. By giving a third parameter <count>, you can limit the
       output to the first <count> lines, followed by ...  if there are
       more.

       These parameters can also be set individually with
       --stat-width=<width>, --stat-name-width=<name-width> and
       --stat-count=<count>.
例如,通过将输出值设置为非常大的数:
git diff --stat=10000

请注意,该命令生成相对于git存储库根目录的路径。

(对于脚本编写,您可能希望直接使用git diff-tree,因为它是一种更为“管道化”的命令,尽管我认为您无论如何都可以使用。请注意,在使用git diff-tree时需要使用--stat和相同的额外文本。使用git diff“清晰明了”的前端与使用git diff-tree的“管道”命令之间的基本区别在于,git diff查找配置设置中的选项(例如diff.renames)来决定是否进行重命名检测。这意味着git diff将读取您的配置并自动调用正确的“管道”命令,而git diff-tree则不会。此外,如果您正在比较提交和索引,前端git diff将执行git diff-index的等效操作。换句话说,git diff会“读取您的配置”并“自动调用正确的“管道”命令”。)


9
git diff --numstat与diff-tree相同。 - cmcginty
1
请注意,为了限制最后一部分(+++/---)的宽度,您可以使用单独的 --stat-graph-width=... 开关。还要注意,设置高 --stat-graph-width=--stat-name-width= 是不够的,您还必须将 --stat-width= 设置得足够大以覆盖这两个开关。 - jakub.g
5
有没有办法全球化这个?每次打字都很疯狂。 - Rudie
@Rudie: 唉,不行:有一个配置变量diff.statGraphWidth可以用来设置--stat-graph-width的值,但其他的默认是你的终端宽度。(所以,另一个答案是:“是的,只要把你的终端窗口调整为1000列宽” :-) ) - torek
1
@bfontaine:是的:git diff-tree总是比较存储库内部的两个现有树对象。也就是说,它完全不能查看存储库之外的内容,也不能查看索引,因此在这里不可用--no-index选项。这有点麻烦,因为git diff没有--porcelain选项!如果您确实需要解决方法,则可以从文件系统中的某个目录创建一个树对象(使用临时索引和git write-tree)。 - torek
显示剩余5条评论

32

对于脚本处理,最好使用以下之一:

# list just the file names
git diff --name-only
path/to/modified/file
path/to/renamed/file


# list the names and change statuses:
git diff --name-status
M       path/to/modified/file
R100    path/to/existing/file   path/to/renamed/file


# list a diffstat-like output (+ed lines, -ed lines, file name):
git diff --numstat
1       0       path/to/modified/file
0       0       path/to/{existing => renamed}/file

当与-z选项结合使用时,它们对于强大的脚本处理变得更加方便,该选项使用NUL作为字段终止符。

Translated content:

当与-z选项结合使用时,它们对于强大的脚本处理变得更加方便,该选项使用NUL作为字段终止符。


根据我的测试,使用这些命令无法获取资源的完整路径。 目前我只能看到已删除文件的相对路径。我不知道这是否仅适用于这些文件。 - GCallie
1
所有输出都将返回相对于 git rev-parse --show-toplevel 的路径。最初的问题是关于截断路径的,这在 diffstats 中是一个问题,特别是对于长文件名或低值的 --stat-name-width。上述命令不会截断路径,但将按请求显示 "完整" 路径,尽管仍相对于仓库根目录。 - cmbuckley

21

对于Bash用户,您可以使用$COLUMNS变量来自动填充可用终端宽度:

git diff --stat=$COLUMNS

非常长的路径名可能仍会被截断;在这种情况下,您可以使用--stat-graph-width来缩小+++ / ---部分的宽度,例如将其限制为终端宽度的1/5:

git show --stat=$COLUMNS --stat-graph-width=$(($COLUMNS/5))

为了得到更通用的解决方案,您可以使用tput cols的输出来确定终端的宽度。


2
有没有办法将 --stat=$COLUMNS,$COLUMNS 全局化?每次都要输入太疯狂了。 - Rudie
@Rudie 将 export COLUMNS 添加到你的 ~/.bashrc 文件中,在你的 ~/.gitconfig 文件中的 [alias] 下添加 smart-diff = ! "gitsmartdiff() { git diff $2 --stat=$COLUMNS,$COLUMNS; }; gitsmartdiff" - user151841
@user151841 这只改变了 diff。我希望它也适用于合并和拉取等操作。(甚至无法在那里手动执行。)我不认为GIT支持它。 - Rudie
1
@Rudie 嗯,在拉取或合并完成后,您可以在先前和新哈希之间进行差异比较。 - user151841
3
当合并数据时,已经会生成一个统计摘要,但是没有参数或配置。如果所有的“统计摘要”都使用相同的配置,那将是很好的。 - Rudie
@Rudie 我创建了一个别名,似乎可以满足你的需求。请在这里查看我的答案:https://dev59.com/DGkv5IYBdhLWcg3wpCQz#55796232 - user151841

8

有一个选项--name-onlygit diff --name-only。该选项也被其他git命令支持,如showstash

使用该选项时,路径不会被缩短。


git diff-tree也有相应的选项,但您需要指定其他选项,如git diff-tree --name-only -r --no-commit-id HEAD。请参阅我的答案https://dev59.com/DGkv5IYBdhLWcg3wpCQz#67330880获取更多信息。 - CervEd

3
我发现一个简单的解决方案是这样的:(只适用于*nix系统,抱歉无法在osx上使用)
git diff --stat=$COLUMNS --relative | head -n -1 | cut -c 2- | xargs -d '\n' -P4 printf "$(pwd)/%s\n"

这个版本适用于两种系统,但在OSX上不太美观。
git diff --stat=$COLUMNS --relative | sed -e '$ d' | cut -c 2- | xargs -n4 -I{} echo "$(pwd)/{}"

1
我发现只使用--relative选项对我非常有帮助。(我已经在使用--stat宽度选项。) - sage

2

git diff是一个用户友好的平面命令,而对于脚本编写目的,您可能想使用相应的底层命令git diff-tree

您可以通过结合--name-only-r--no-commit-id选项让git diff-tree输出相对于Git存储库的全路径。

示例

在当前分支的“上一个”(即HEAD)提交中更改的文件路径。

git diff-tree --name-only -r --no-commit-id HEAD

主分支上最后一次提交的文件路径

git diff-tree --name-only -r --no-commit-id main

主分支上最近三次提交的文件路径

git diff-tree --name-only -r main main~3

查看路径为 src/ 的最新提交的文件路径。

git diff-tree --name-only -r --no-commit-id main src/

当前分支上最后一次提交所更改的文件的绝对路径

git diff-tree --name-only -r --no-commit-id --line-prefix=`git rev-parse --show-toplevel`/ HEAD

解释

git diff-tree 比较两个 treeish 对象的 blobs

提交是 treeish 对象,它指向仓库根目录中的对象。目录也是 treeish 对象,而文件是 blobs

运行 git diff-tree HEAD 将比较 HEADHEAD~1blobs,并包含仓库根目录中的差异。要查看所有未在根目录中的更改文件,我们需要进入目录 treeish 对象。这可以通过使用 -r (即递归) 选项来实现。

请注意,这允许比较任意提交中的任意目录。

默认情况下,如果只指定一个 commit 对象,则将其与其父对象进行比较。即,运行 git diff-tree HEAD 等同于 git diff-tree HEAD HEAD~1。如果您只指定一个提交作为 treeish 对象,则显示父提交 ID。使用 --no-commit-id 可以去除此信息。

git-diff-tree 打印了我们不需要的大量信息(ID、权限、是否为添加、删除或修改)。我们只需要名称,因此我们使用 --name-only

如果我们想要绝对路径,我们需要在所有行前加上类似于 git rev-parse --show-toplevel 的东西。这将获取仓库的绝对路径,不包括尾随的 /。所以我们添加了它。

--line-prefix=`git rev-parse --show-toplevel`/

0
我创建了以下的git别名:
diffstat = ! "gitdiffstat() {  git diff --stat=$(tput cols) ${1:-master} ; }; gitdiffstat"

它从tput cols命令中读取列数。默认情况下,它将与master进行比较,但您可以选择指定另一个分支。

$ git diffstat
 .gitalias | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

-1

我发现 diff --stat 命令的行为在 git 1.7.10 左右发生了变化,之前默认情况下它会将文件路径缩短到固定的宽度 - 现在它会显示尽可能多的内容以适应终端窗口。如果您遇到这个问题,请确保升级到 1.8.0 或更高版本。


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