我可以在未跟踪的文件上使用git diff吗?

375

有没有可能让git diff在其差异输出中包含未跟踪的文件,或者我最好的选择是对新创建的文件和我已编辑的现有文件使用git add,然后使用:

git diff --cached
12个回答

355

使用最新的git版本你可以使用git add -N命令(或者--intent-to-add选项),它会在索引中添加一个零长度的blob。这意味着你的“未跟踪”文件现在变成了修改,以添加所有内容到这个零长度的文件中,并且这会显示在“git diff”输出中。

git diff

echo "this is a new file" > new.txt
git diff

git add -N new.txt
git diff
diff --git a/new.txt b/new.txt
index e69de29..3b2aed8 100644
--- a/new.txt
+++ b/new.txt
@@ -0,0 +1 @@
+this is a new file

不幸的是,正如所指出的那样,当您有一个挂起的--intent-to-add文件时,无法执行git stash操作。但是如果您需要stash,可以先将新文件添加,然后再stash它们。或者您可以使用仿真解决方法:

git update-index --add --cacheinfo \
100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 new.txt

(在这里设置别名会对你有帮助)。


1
结果发现我的 Git 版本不够新,无法使用 add -N 命令,但这回答了我的问题。 - Andrew Grimm
1
你可以使用“git update-index --add --cacheinfo 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 new.txt”来模拟“git add -N new.txt”。 - araqnid
2
如果你有很多新文件,是否有一种简单的方法将它们全部添加,然后进行差异比较? - Vic
2
@Vic git add -N . - Nathan
1
你也不能使用 git restore 命令来恢复那些文件。这个命令只会截断所有被标记为“意图添加”的文件! - Anthony
显示剩余7条评论

138

我认为你可以通过简单提供两个文件的路径来比较你索引中的文件和未跟踪的文件。

git diff --no-index tracked_file untracked_file

4
如果你在上次提交之后创建了多个未跟踪的文件,这个方法还有效吗? - Andrew Grimm
18
是的,完美的答案!我随后可以使用git diff --no-index untracked_file_1 untracked_file_2来获取git diff的语法着色等效果......太好了。 - Colin D Bennett
62
我不明白你为什么要将一个已跟踪的文件与无关的未跟踪的文件进行比较。 如果你只是想获取未跟踪文件的差异输出,可以使用/dev/nullgit diff --no-index -- /dev/null <未跟踪的文件> - user456814
7
或者只需输入cat untracked_file_1,或者如果您真的需要绿色输出,则可以使用printf '\e[1;32m%s\e[0m\n' "$(cat untracked_file_1)"。请注意,命令替换将删除文件中的尾随换行符。 :) - Wildcard
13
这应该是被接受的答案——它不需要改变git的索引;正如原作者所说,这样做有其缺点。 - Byte Lab
显示剩余6条评论

54

虽然不是完全针对问题的解决方案,但如果由于某种原因您不想按照被接受的答案建议的方式将文件添加到索引中,则还有另一种选择:

如果这些文件未被跟踪,则显然差异是整个文件,因此您可以使用less查看它们:

less $(git ls-files --others --exclude-standard)

使用:n:p来在它们之间导航,前进和后退。

从注释中更新: 如果需要补丁格式,您也可以将其与git diff结合使用:

git ls-files --others --exclude-standard -z | xargs -0 -n 1 git --no-pager diff /dev/null | less

在这种情况下,您还可以将输出重定向到文件或使用其他diff命令。


7
你也可以运行 git diff /dev/null <未跟踪的文件> 来获取补丁格式的补丁,而不仅仅是一个文件。 - SimSimY
3
这个答案结合了git diff是完美的解决方案。 - iwind
1
这里的第二个命令行非常好用。我喜欢保留颜色,所以在单词“diff”后面加上了“--color=always”,并在less中使用了“-R”。所以全部命令如下:git ls-files --others --exclude-standard | xargs -n 1 git --no-pager diff --color=always /dev/null | diff-so-fancy | less -R - Tyler Collier
2
@TylerCollier 需要在 git 命令中添加 -z,并在 xargs 中添加 -0。尽可能始终使用空终止符以避免出现文件名包含回车符或其他奇怪字符等问题(以及安全漏洞!)。 - ErikE
看起来使用这种方法时,我没有看到通常在git diff中看到的颜色高亮显示。 - undefined

45
对于我的交互式日常 gitting(我一直在 diff 工作树和 HEAD,希望未跟踪的文件被包含在 diff 中),add -N/--intent-to-add 是不可用的,因为它会 破坏 git stash。所以这是我使用的 git diff 替换方案。它并不是特别干净的解决方案,但因为我真的只是交互式地使用它,所以我可以接受这个 hack:
d() {
    if test "$#" = 0; then
        (
            git diff --color
            git ls-files --others --exclude-standard |
                while read -r i; do git diff --color -- /dev/null "$i"; done
        ) | `git config --get core.pager`
    else
        git diff "$@"
    fi
}

在我的工作流中,只键入d将包括未跟踪的文件在内,这是我关心的差异之处;而d args...则像常规的git diff一样运行。

注:

  • 这里使用了一个事实,即git diff实际上只是串联的单个差异,因此无法区分d输出和“真正的差异”——除了所有未跟踪的文件都被排在最后的事实。
  • 这个函数唯一的问题是,即使重定向,输出也会带有颜色;但是我懒得为此添加逻辑。
  • 我找不到任何方法来通过组装一个简洁的参数列表来包含未跟踪的文件git diff。如果有人发现如何做到这一点,或者如果将来git添加了某个功能,请在此留言!

4
具有讽刺意味的是,我之前建议旧版Git使用git update-index --add --cacheinfo 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 new.txt的变通方法,在使用git stash时确实有效,假设你已经在数据库中拥有了e69de29bb,例如之前尝试过add -N。因此,显然在某种程度上它与git add -N不完全相同:说实话,我不确定具体有何区别。 - araqnid
2
你正在使用字符串比较而非数字相等检查来执行test命令。这不会影响任何事情,但是 test "$#" -eq 0 更精确地达到了预期的目的。 - Wildcard
2
是的,看起来仍然需要一对一地执行...但可以伪装成一个“less”,这样您就不必为每个文件按 q ,并且感觉就像 git diff 一样,通过去除每个文件的分页(-P),然后再添加回来(| less),保留颜色(--color=always)并将其解释为颜色(less -rless -R)。因此,总体来说是这样的:do git -P diff --color=always -- /dev/null "$i"; done | less -r - hyperpallium
如果您将来希望受到打扰,test -t 1(例如 if [ -t 1 ]; then color_arg=--color; fi 或类似的方式)是 shell 检查其输出是否为终端的一种方法,这是决定颜色的有用方式。而 xargs 可能会提供一种摆脱 while 循环的方法。您仍然需要在其中使用 -n 1,因此它仍然会多次启动 git,并且仍然需要成对使用,但是... 它可以摆脱 whileread,所以也许更好?!?我把这个决定留给读者。 - lindes

38
git add -A
git diff HEAD

如果需要生成补丁,请执行以下操作:

git reset HEAD

5
这可能会因为将所有内容添加进去而丢失原来的工作,尤其是经常使用 git add -p 命令的情况下(顺便说一句,我通常推荐使用这个命令)... 这确实提供了一种基本的操作方式,但需要注意的是它可能会带来意想不到的负面影响。 - lindes

23

对于一个文件:

git diff --no-index /dev/null new_file

对于所有新文件:

for next in $( git ls-files --others --exclude-standard ) ; do git --no-pager diff --no-index /dev/null $next; done;

作为别名:

alias gdnew="for next in \$( git ls-files --others --exclude-standard ) ; do git --no-pager diff --no-index /dev/null \$next; done;"

对于所有修改过和新建的文件,作为一个命令合并:

{ git --no-pager diff; gdnew }

3
这是一个绝妙的答案!比那个被采纳的答案好多了! - Jeremy Moritz
1
我还为最后一个命令添加了这个别名:alias gdall="git --no-pager diff; gdnew" - Jeremy Moritz
给出的第一个示例在Git for Windows中无法工作:$ git diff --no-index -- /dev/null .gitignore在我的情况下,结果是:error: Could not access 'device_support/oem/renesas/rx72n-envision/e2studio-CCRX/R5F572NN/nul' - AJM

16

对我来说这个方法有效:

git add my_file.txt
git diff --cached my_file.txt
git reset my_file.txt

最后一步是可选的,它将使文件保持在之前的状态(未被追踪)

如果你也在创建一个补丁,这个步骤就会很有用:

  git diff --cached my_file.txt > my_file-patch.patch

这个添加/重置对的特殊性与 https://dev59.com/j3RA5IYBdhLWcg3wtwSe#50486906 的“散弹枪”方法形成了很好的对比...感谢您。 - lindes

6
更新:我的答案适用于分阶段的和未分阶段的更改,而不是已跟踪的和未跟踪的更改。有关已跟踪/未跟踪信息,请参见已接受的答案。这里保留作为纪念。

以下仅提供未暂存更改:

$ git diff

如果您想要同时包含已分阶段和未分阶段的更改,请在命令中添加HEAD
$ git diff HEAD

44
HEAD默认值,因此与不解决问题的 git diff 相同。 - Iulian Onofrei
7
这需要使用git add命令将每个未被追踪的文件加入版本控制。 - SilvioQ
如果您编辑此答案以包括 git add,则是最简单的——如果您的用例是检查您刚刚添加/想要添加的内容。 - KCD
@lulian 错了,git diff 只会显示未暂存的更改,git diff HEAD 会显示已暂存和未暂存的更改。 - ruohola
3
这并没有回答问题,问题是关于“未跟踪”而不是“未暂存”。 - Nick
同意。我会进行更新。不是针对我的回答,因为我认为我的帖子仍然可以帮助其他人,而是为了澄清并将他们指向原始问题的正确答案。 - alairock

3

利用将新文件分阶段(staging)的思想,再使用diff工具比较已被分阶段(staged)的文件,两者结合即可查看差异。我认为这种方法很简单易用。

  1. Add the files you want to see the diff.In your case, add only untracked files. You can optionally choose to add only those files you want to see the diff for.

    git stash && git add . && git stash pop 
    
  2. Diff the staged

    git diff --staged
    
  3. Reset the staged files if needed

    git reset
    

综合上述所有内容,

   git stash && git add . && git stash pop && git diff --staged && git reset 

2
通常当我与远程团队合作时,对我来说很重要的是,在我按照 git 阶段 untrack->staged->commit 进行操作之前,我需要事先了解其他团队在同一文件中所做的更改。为此,我编写了一个 bash 脚本,帮助我避免与远程团队不必要地解决合并冲突,或者创建新的本地分支并在主分支上进行比较和合并。
#set -x 
branchname=`git branch | grep -F '*' |  awk '{print $2}'`
echo $branchname
git fetch origin ${branchname}
for file in `git status | grep "modified" | awk "{print $2}" `
do
echo "PLEASE CHECK OUT GIT DIFF FOR "$file 
git difftool FETCH_HEAD $file ;
done

在上述脚本中,我获取远程主分支(不一定是主分支)到FETCH_HEAD,然后仅列出我修改的文件,并将修改的文件与git difftool进行比较。
git支持许多difftools。我配置了'Meld Diff Viewer'以进行良好的GUI比较。

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