Git diff显示子项目有更改

302

我刚刚运行了一个 git diff 命令,对于我所有的约10个子模块都得到了以下输出

diff --git a/.vim/bundle/bufexplorer b/.vim/bundle/bufexplorer
--- a/.vim/bundle/bufexplorer
+++ b/.vim/bundle/bufexplorer
@@ -1 +1 @@
-Subproject commit 8c75e65b647238febd0257658b150f717a136359
+Subproject commit 8c75e65b647238febd0257658b150f717a136359-dirty

这是什么意思?我该如何修复它?

11个回答

323

2021年1月更新,十年后:

"git diff"(man) 显示子模块工作树有未跟踪的杂物作为 Submodule 提交 <objectname>-dirty,但自然的期望是 "-dirty" 标识符应与 "git describe --dirty"(man) 对齐,而它不认为在工作树中有未跟踪的文件是污点的来源。
这种不一致已经在 Git 2.31 (2021年第一季度) 中得到修复。

请查看由Sangeeta Jain (sangu09)于2020年11月10日提交的commit 8ef9312
(由Junio C Hamano -- gitster --于2021年1月25日合并,commit 0806279)

diff: 不要将带有未跟踪文件的子模块显示为“-dirty

签署者:Sangeeta Jain

Git diff会将子模块目录报告为“-dirty”,即使子模块目录中只有未跟踪的文件。这与在该状态下在子模块目录中运行git describe --dirty(man)所说的不一致。当没有配置变量或命令行选项时,使--ignore-submodules=untracked成为git diff(man)的默认值,以便该命令不会给具有未跟踪文件的工作树的子模块添加“-dirty”后缀,以使其与在子模块工作树中运行的git describe --dirty保持一致。同时,也将--ignore-submodules=none设置为git status(man)的默认值,以使用户不会删除具有未提交(未跟踪)文件的子模块。

git config现在在其手册页中包含:

默认情况下,此设置为未跟踪,因此将忽略任何未跟踪的子模块。


原始答案(2011年)

如Mark Longair的博客文章Git子模块解释中所提到的,

git 1.7.0及更高版本包含了一个讨厌的变化,即:如果git子模块有任何修改过的文件或未跟踪的文件,则将其视为脏文件。而以前只有在子模块中的HEAD指向错误提交时才会出现这种情况。

git子模块输出中加号符(+)的含义已经改变,第一次遇到这种情况需要花费一些时间来弄清楚出了什么问题,例如查看更改日志或使用git.git上的git bisect来查找更改。对于用户来说,引入一个不同的符号表示“在指定的版本上,但是有更改”会更友好。

你可以通过以下方式修复它:

  • 在返回父存储库之前,要么提交或撤消每个子模块中的更改/演进,这样差异就不应该报告“脏”文件了。要撤消对子模块的所有更改,只需cd进入子模块的根目录并执行git checkout .

dotnetCarpenter 评论道你可以执行以下命令:git submodule foreach --recursive git checkout .

  • 或者在你的git diff命令中添加--ignore-submodules,暂时忽略那些“脏”的子模块。

Git版本1.7.2中的新功能

正如Noam评论的, this question提到自从git版本1.7.2以来,你可以使用以下命令忽略“脏”的子模块:

git status --ignore-submodules=dirty

3
还有一件需要知道的好事:即使不必担心添加这些更改,您仍然可以执行git commit -a。虽然它们在前面标有M,但它们不会出现在您的提交中。 - gitaarik
2
对我来说,我必须进入每个脏的子模块并运行 git clean -id - GDP2
2
@GDP2 可以在一行命令中完成,使用 git submodule foreach --recursive git clean -id(请先在备份仓库中进行测试;) - VonC
2
你10年更新的答案把我搞糊涂了。不过你10年前的原始答案还是适用的:git submodule foreach --recursive git checkout . - eduncan911
2
一个解决方法是在受影响的子模块中执行“Stash changes”(TortoiseGit菜单项)。这应该会删除您的未跟踪文件并将它们保存到您的存储列表中。然后,您应该能够正常提交而不会显示为脏。一旦您提交了更改,就可以使用“Stash pop”还原您的文件。 - John
显示剩余3条评论

35

要忽略任何子模块中的所有未跟踪文件,请使用以下命令来忽略这些更改。

git config --global diff.ignoreSubmodules dirty

它将向您的本地git配置文件中添加以下配置选项:

[diff]
  ignoreSubmodules = dirty

更多信息可以在这里找到。


政治上最正确的建议可能是“为什么不保持你的子模块干净呢?”无论如何,这个解决方案是否仍然显示修改过的文件?从哪个Git版本开始可以执行此过程? - grenix

32

同时移除子模块,然后运行 git submodule initgit submodule update 显然也可以解决问题,但这种方法可能并不总是适当或可行。


2
在我将一些现有文件夹转换为子模块,然后拉入另一台仍具有旧文件夹的机器时,这对我起了作用。 - Roger Lipscombe

21

编辑: 这个答案(以及大部分其他的答案)已经过时了;请参考Devpool的答案


最初,并没有配置选项可以使"git diff --ignore-submodules"和"git status --ignore-submodules"成为默认全局选项(但请参见在命令上设置git默认标志)。另一种方法是在您想要忽略的每个子模块上设置一个默认的ignore配置选项(对于git diffgit status都是如此),可以在.git/config文件(仅本地)或.gitmodules中进行设置(将由git进行版本管理)。例如:

[submodule "foobar"]
    url = git@bitbucket.org:foo/bar.git
    ignore = untracked

ignore = untracked表示忽略未跟踪的文件,ignore = dirty表示忽略已修改的文件,ignore = all表示忽略所有提交。显然无法使用通配符来匹配所有子模块。


就 Git 2.31 而言,即使是此编辑帖子中链接的推荐答案似乎已经过时。无论如何,有关 git 版本的大多数上下文信息都在被接受的答案中。仍然不确定该怎么做!(我在我的 Ubuntu LTS 中有 Git 2.17) - grenix
1
我在Git 2.17中尝试了像这个解决方案一样修改.gitmodules,并且它达到了预期的效果。也许我错了,但我最喜欢这个解决方案,因为.gitmodules将由git进行版本控制。(+1)如果按照推荐答案的方式进行操作,则应该执行git config --global diff.ignoreSubmodules untracked而不是git config --global diff.ignoreSubmodules dirty - grenix

16

这是因为你拥有的子模块指针并不是实际位于子模块目录中的指针。要解决这个问题,你必须再次运行 git submodule update 命令:


15
git submodule foreach --recursive git checkout .

这对我来说没起作用,但它给了我一个文件列表(在我这里只有一个),显示子模块中已更改的文件(而我在那里没有做任何事情)。

因此,我可以进入子模块,git status显示我的HEAD是分离状态-> git checkout master,再次git status以查看修改的文件,git checkout >文件名<,git pull,一切都正常了。


15

我最终删除了子模块目录并重新初始化它。

cd my-submodule
git push
cd ../
rm -rf my-submodule
git submodule init
git submodule update

4
我更希望理解发生了什么,但这也是唯一对我有效的事情... - smilebomb
有时候斧头是最好的工具 :) 推送后,我会更放心地使用“git status”来查看是否一切都干净。 (只是在偏执的日子里,我会将my-submodule移动到回收站) - grenix

7

如果启用了文件模式设置并且您更改了子模块子树中的文件权限,则子模块可能会被标记为脏。

要禁用子模块中的文件模式,可以编辑/.git/modules/path/to/your/submodule/config并添加

[core]
  filemode = false

如果您想忽略所有脏状态,可以在/.gitmodules文件中设置 ignore = dirty property,但我认为仅禁用文件模式更好。


谢谢您提供的提示,文件模式更改也可能引起这种行为,但是在子模块中通常不建议使用文件模式吗? - grenix

2

你是否有足够的权限访问你的代码库?

我的解决方案与git无关,但我看到了相同的错误消息和子模块的状态。

根本原因是.git文件夹中的某些文件属于root,因此git没有写入权限,因此当以我的用户身份运行时,git无法更改子模块的状态。

你是否遇到了同样的问题?

从你的代码库根目录,使用find列出由root拥有的文件[可选]

find .git -user root

解决方案 [Linux]

.git 文件夹中的所有文件的所有者更改为

sudo chown -R $USER:$USER .git

# alternatively, only the files listed in the above command...
sudo find .git -user root -exec chown $USER:$USER {} +

这是怎么发生的?

在我的情况下,我从一个docker容器中构建了子模块的库。docker守护进程传统上以root身份运行,因此创建的文件归属于root:root

通过该服务,我的用户具有代理的root权限,因此即使我没有使用sudo,我的git仓库仍然有由root拥有的更改。

我希望这能帮助到某些人,现在可以离开了。


谢谢分享,希望我在遇到这种情况时能记住这个。如果所有版本的Git都对这种用例有如此不具体的错误消息,那将是很有趣的事情。 - grenix

2

想知道这个解决方案是否与此处提到的解决方案相同:https://dev59.com/0W445IYBdhLWcg3waplH#43324275?这个解决方案是否也会删除未跟踪的文件? - grenix

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