自 2011 年以来,Git v1.8.3-rc0(2013 年 4 月)首次修复了此问题。
它修复了代码中的一些问题,以遍历工作树以查找未跟踪和/或忽略的文件,清理和优化代码路径。
见commit 0aaf62b、commit defd7c7、commit 8aaf8d7、commit b07bc8c、commit 95c6f27、commit 6cd5c58、commit 46aa2f9、commit 5bd8e2d、commit 5bd8e2d、commit be8a84c、@9876543321、@、@9876543321 @、commit 289ff55、commit 560bb7a(2013 年 4 月 15 日)Karsten Blees (kblees)。
(由 Junio C Hamano -- gitster -- 合并到 commit 7093d2c,2013 年 4 月 23 日)
dir.c: 让 'git-status --ignored' 在主要目录中工作
签字人:Karsten Blees
如果“path”的某些组件被归类为未跟踪,则“git status --ignored path/”不会列出“path”中被忽略的文件和目录。
在遍历前导目录时禁用DIR_SHOW_OTHER_DIRECTORIES 标志。这可以防止带有DIR_SHOW_IGNORED 标志的treat_leading_path() 在顶级未跟踪目录中止。
作为副作用,这也消除了每个前导目录级别的递归目录扫描,因为treat_directory() 在从treat_leading_path() 调用时不能再调用read_directory_recursive()。
但是,6 年后(2019 年末),随着 Git 2.25(2020 年第一季度),对目录遍历 API 的各种修复......恢复上面看到的那个修复,并重新实现它。
参见Junio C Hamano (gitster)commit 6836d2f(2019 年 12 月 20 日)。
请参阅commit c847dfa、commit 777b420、commit b9670c1(2019 年 12 月 19 日)和commit c5c4edd、commit 072a231、commit 2f5d384、commit a2b1336、commit 452efd1(2019 年 12 月 10 日)@9876543。 >(由 Junio C Hamano -- gitster -- 合并于 commit d2189a7,2019 年 12 月 25 日)
Revert "dir.c: 使 'git-status --ignored' 在主要目录中工作"
签字人:Elijah Newren
提交be8a84c52669 ("[dir.c](https://github.com/git/git/blob/a2b13367fe55bdeb10862f41aff3e2446b63e171/dir.c): 使'git-status --ignored'在主要目录中工作", 2013-04-15,Git v1.8.3-rc0 -- merge) 注意到
git status --ignored <SOMEPATH>
如果<SOMEPATH> 未被跟踪,则不会列出<SOMEPATH> 中被忽略的文件和目录,并修改行为以使其显示它们。
但是,它是通过破坏一致性的 hack 实现的;它会以不同于简单的方式显示<SOMEPATH> 下的路径
git status --ignored | grep <SOMEPATH>
会向他们展示。
正确的修复会稍微复杂一些,并且由于这个 hack 稍微复杂一些,所以我们恢复了这个提交(但保留了测试用例的更正版本),稍后将通过后续补丁修复原始错误。
一些历史记录可能会有所帮助:
在commit 48ffef966c76 ("ls-files: fix overeager pathspec optimization", 2010-01-08, Git v1.7.0-rc0 -- merge );但它实际上是朝着相反的方向发展。
在那次提交中,它提到了如何
git ls-files -o --exclude-standard t/
用于在 t/ 下显示未跟踪的文件,即使 t/ 被忽略,然后更改行为以停止在忽略的目录下显示未跟踪的文件。
更重要的是,此提交考虑保留此行为,但指出它与指定多个路径规范时的行为不一致并因此拒绝它。
当指定一个路径规范与零或两个路径规范时,整个不一致的原因是因为路径规范的公共前缀是通过一组不同的检查(在treat_leading_path())发送的,而不是正常的文件/目录遍历(那些通过@987654395 @ 和 treat_path())。
因此,为了保持一致性,需要检查两个代码路径是否产生相同的结果。
还原commit be8a84c526691667fc04a8241d93a3de1de298ab,除了不删除它添加的测试用例,修改它以检查正确和一致的行为。
还有:
dir:修复对公共前缀目录的检查
签字人:Elijah Newren
许多年前,目录遍历逻辑有一个优化,它总是递归到作为所有路径规范的公共前缀的任何目录,而无需遍历前导目录以到达所需目录。
因此,
git ls-files -o .git/ # case A
会注意到.git/ 是所有路径规范的公共前缀(因为它是列出的唯一路径规范),然后遍历它并开始显示该目录下的未知文件。
不幸的是,.git/ 不是我们应该遍历的目录,这使得这种优化存在问题。
这也影响了以下情况:
git ls-files -o --exclude-standard t/ # case B
t/ 在 .gitignore 文件中的位置,因此不有趣,不应该被递归到。
它还影响了以下情况:
git ls-files -o --directory untracked_dir/ # case C
untracked_dir/ 确实是未跟踪的,因此很有趣,但 --directory 标志意味着我们只想显示目录本身,而不是递归到它并开始在它下面列出未跟踪的文件。
在提交16e2cfa90993 ("read_directory(): 进一步拆分treat_path()", 2010-01-08) 和48ffef966c76 ("ls-files: 修复过度的路径规范优化" , 2010-01-08, Git v1.7.0-rc0 -- merge),想法是我们首先想检查公共前缀是否有趣。
前一个补丁指出treat_path() 在检查公共前缀时不能使用,因为treat_path() 需要dir_entry(),而我们在检查公共前缀时还没有读取任何目录。
因此,该补丁将treat_one_path() 与treat_path() 分开。
后一个补丁创建了一个新的treat_leading_path(),它手动复制了treat_path() 中无法分解的位,然后调用treat_one_path() 其余部分。
这种方法存在三个问题:
-
treat_leading_path() 中的重复逻辑意外错过了对特殊路径的检查(例如 is_dot_or_dotdot 和匹配的“.git”),导致 case A 类型的错误继续成为问题。
-
treat_leading_path() 逻辑假设我们应该遍历 path_treatment 不是 path_none 的任何地方,即它会导致 C 类错误持续存在。
- 这意味着我们有需要保持同步的拆分逻辑,冒着人们引入新的不一致的风险(例如在commit be8a84c52669,我们在本系列的前面恢复了,或者在commit df5bcdf83ae,我们将修复在随后的提交中)
通过使treat_leading_path() 不仅在每个前导路径组件上循环,而且直接在每个路径上调用treat_path() 来解决大多数这些问题。
为此,我们必须创建一个合成的dir_entry,,但这只需要几行代码。
那么,注意我们从treat_path()得到的path_treatment结果,不要把path_excluded,path_untracked,和path_recurse都和path_recurse一样对待。