`git clean`默认删除被忽略的文件?

bas*_*sin 11 git

根据帮助,没有-x选项git clean应该更不用说被忽略的文件,但事实并非如此.

[il@reallin test]$ cat .gitignore
*.sar
[il@reallin test]$ mkdir -p conf/sar && touch conf/sar/aaa.sar
[il@reallin test]$ git status
# On branch master
nothing to commit, working directory clean
[il@reallin test]$ git clean -df
Removing conf/
Run Code Online (Sandbox Code Playgroud)

conf/sar/aaa.sar已移除.这是一个错误吗?

mvp*_*mvp 5

根据man git clean:

-d
    Remove untracked directories in addition to untracked files.
Run Code Online (Sandbox Code Playgroud)

在您的情况下,conf/sar不跟踪目录- 它不包含由git跟踪的任何文件.如果你没有gitignore规则并执行git clean -fd,那么这个未跟踪的目录的内容将被删除 - 只是文档说的.

现在,如果您添加.gitignore规则来忽略*.sar文件,它不会改变您的目录conf/sar/仍未跟踪的基本事实,并且具有符合此gitignore规则的未跟踪文件aaa.sar不应突然使其无法移除git clean -fd.

但是,如果您在忽略旁边添加任何跟踪文件aaa.sar,则不会删除此目录,并且您的文件将保持不变.

换句话说,虽然它看起来很混乱,但这不是一个bug,git正是文档所说的.

  • 附带说明:最好在 `sar` 目录中放置一个忽略 `*.sar` 文件的 `.gitignore`。这不仅使顶级`.gitignore` 更清晰,并在需要的地方保留忽略信息,它还具有保持该目录活动的额外好处,如@mvp 所述。 (2认同)

Von*_*onC 5

警告:此git clean行为将在 Git 2.14(2017 年第 3 季度)中略有改变

\n\n

"git clean -d"用于清理已忽略文件的目录,即使该命令不应丢失没有 " -x" 的被忽略的文件。
\n" git status --ignored" 不会列出没有 " -uall" 的被忽略和未跟踪的文件。

\n\n

请参阅提交 6b1db43(2017 年 5 月 23 日),以及提交 bbf504a提交 fb89888提交 df5bcdf提交 0a81d4a提交 b3487cc(2017 年 5 月 18 日),作者:Samuel Lijin ( sxlijin)
\n (由Junio C Hamano 合并 -- gitster--提交 f4fd99b,2017 年 6 月 2 日)

\n\n
\n

clean:教clean -d保留被忽略的路径

\n\n

有一个隐含的假设,即仅包含未跟踪和忽略路径的目录本身应被视为未跟踪。这在我们询问是否应该将目录添加到 git 数据库的用例中是有意义的,但在我们询问是否可以安全地从工作树中删除目录时则不然;因此,clean -d会假设可以删除包含被忽略路径的“未跟踪”目录,即使这样做也会删除被忽略的路径。

\n\n

为了解决这个问题,我们教导clean -d收集被忽略的路径,并跳过未跟踪的目录(如果它包含被忽略的路径),而只是删除其中未跟踪的内容。
\n 要实现此目的,cmd_clean()除了所有忽略的路径之外,还必须收集未跟踪目录的所有未跟踪内容,以确定必须跳过哪些未跟踪的目录(因为它们包含忽略的路径)以及不应跳过哪些未跟踪的目录

\n
\n\n

但是......自 2017 年以来,这种变化意味着git status --ignored无限期地挂起\nMartin Melka该线程
中报道,并由SZEDER G\xc3\xa1bor分析:

\n\n
\n

如果目录深度为 120 个,则需要超过 6*10^23 年才能完成

\n\n

这种速度减慢是由提交 df5bcdf引起的,该提交是修补程序系列的一部分,用于修复git clean -d删除未跟踪的目录,即使它们包含被忽略的文件。

\n
\n\n

所以...修复正在进行中,将于 2020 年晚些时候发布。

\n\n
\n\n

Git 2.24(2019 年第 4 季度)说明了这种git clean行为变化引入了回归。

\n\n

请参阅SZEDER G\xc3\xa1bor ( )的提交 502c386(2019 年 8 月 25 日)。\n (由Junio C Hamano 合并 -- --提交 026428c中,2019 年 9 月 30 日)szeder
gitster

\n\n
\n

t7300-clean:演示删除嵌套存储库并忽略文件损坏

\n\n

git clean -fd如果未跟踪的目录属于不同的 Git 存储库或工作树, \' \' 不得删除该目录。

\n\n

不幸的是,如果.gitignore外部存储库中的“\”规则恰好与嵌套存储库或工作树中的文件匹配,那么就会出现问题,并且“\ git clean -fd”确实会删除嵌套存储库工作树的内容\n,除了忽略文件,可能导致数据丢失。

\n\n

添加一个测试到\' t7300-clean.sh\'来演示这种破坏。

\n\n

此问题是6b1db43中引入的回归问题(clean:teach\n clean -d保留忽略的路径,2017-05-23,Git v2.13.2)。

\n
\n\n
\n\n

Git 2.24 进一步明确git clean -d

\n\n

请参阅提交 69f272b(2019 年 10 月 1 日),并提交 902b90c提交 ca8b539提交 09487f2、提交e86bbcf提交 3aca580提交 29b577b提交 89a1f4a提交 a3d89d8提交 404ebce提交 a5e916c提交bbbb6b0提交 7541cc5(2019 年 9 月 17 日)作者:伊利亚·纽伦 (Elijah Newren newren)
\n (由Junio C Hamano 合并 -- gitster--提交 aafb754中,2019 年 10 月 11 日)

\n\n
\n

t7300:添加测试用例,显示清理指定路径规范失败

\n\n

有人给我带来了一个测试用例,其中有多个git-clean需要多次调用才能清除不需要的文件:

\n\n
mkdir d{1,2}\ntouch d{1,2}/ut\ntouch d1/t && git add d1/t\n
Run Code Online (Sandbox Code Playgroud)\n\n

通过此设置,用户需要运行

\n\n
git clean -ffd */ut\n
Run Code Online (Sandbox Code Playgroud)\n\n

两次删除两者ut文件。

\n\n

一些测试显示了一些有趣的变体:

\n\n
    \n
  • 如果这两个 ut 文件中仅存在一个(其中之一),则只需要一个 clean 命令。
  • \n
  • 如果两个目录都有跟踪文件,则只需要一次 git clean 即可清理这两个文件。
  • \n
  • 如果两个目录都没有跟踪文件,那么上面的 clean 命令永远不会清除任何一个未跟踪的文件,尽管 pathspec 显式调用了这两个文件。
  • \n
\n\n

一等分显示清理文件的失败始于提交cf424f5(“ clean:尊重路径规范与” -d“,2014-03-10,Git v1.9.1)。
\n但是,这指出了一个单独的问题:而“ -d“ 标志是由向我展示此问题的原始用户使用的,该标志应该与此问题无关。
\n 不使用“ ”标志再次测试-d表明,在不使用该标志的情况下也存在相同的错误行为,并且实际上已经存在从cf424f5之前开始。

\n
\n\n

所以:

\n\n
\n

clean:使用“ -d”尊重路径规范

\n\n

git-clean使用 read_directory 来填充struct dir具有潜在命中的 a 。然而,read_directory 实际上并不检查我们的路径规范。它使用可能会出现误报的简化版本。因此,我们需要检查所有命中是否与我们的路径规范匹配。

\n\n

我们对非目录可靠地这样做。

\n\n

对于目录,如果没有给出“-d”,我们会检查路径规范是否完全匹配(即,我们甚至更严格,并且需要显式的“ git clean foo”来清理“ foo/”)。但是如果-d给出了“”,我们根本不检查路径规范,而不是放宽精确匹配以允许递归匹配。

\n\n

此回归在113f10f中引入(将 git-clean 设为内置,2007-11-11,Git v1.5.4-rc0)。

\n\n

dir:如果我们的路径规范可能匹配目录下的文件,请递归到该目录

\n\n

对于git clean,如果一个目录完全未被跟踪并且用户没有指定-d(对应于DIR_SHOW_IGNORED_TOO),那么我们通常不想删除该目录,因此不会递归到它。

\n\n

但是,如果用户在该目录下的某个位置手动指定要删除的特定(甚至是通配符)路径,那么我们需要递归到该目录以确保按照用户请求删除该目录下的相关路径。

\n\n

请注意,这并不意味着将添加 recursed-into 目录dir->entries以便稍后删除;从本系列前面的一些提交开始,从递归目录返回后,在决定将其添加到条目列表之前,会运行另一个更严格的匹配检查。
\n 因此,这只会导致给定目录下与路径规范之一匹配的文件被添加到条目列表中。

\n
\n\n

和:

\n\n
\n

dir:还检查目录是否匹配路径规范

\n\n

即使目录与路径规范不匹配,根据精确的路径规范,它下面的某些文件也可能会匹配。
\n 因此,我们针对这种情况进行特殊处理并递归到目录中。
\n 但是,我们之前总是将递归到的任何未跟踪目录添加到未跟踪路径列表中,无论目录本身是否与路径规范匹配。

\n\n

对于“ ”和“ ”git-clean的一组路径规范的情况,\n 这会导致问题,因为我们最终会得到以下两者的目录条目:dir/filemore

\n\n
"dir"\n"dir/file"\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后correct_untracked_entries()会尝试通过删除“”来帮助我们删除重复项,dir/file因为它位于“ dir”下,给我们留下

\n\n
"dir"\n
Run Code Online (Sandbox Code Playgroud)\n\n

由于原始路径规范只有“ dir/file”,因此剩下的唯一条目不匹配,并且没有任何内容可以删除。
\n (请注意,如果仅指定了一个路径规范,例如仅指定了“ dir/file”,那么common_prefix_len中的优化fill_directory将导致我们绕过此问题,使其出现在我们可以正确删除手动指定的路径规范的简单测试中。)

\n\n

通过实际检查我们要添加到目录条目列表中的目录是否确实与路径规范匹配来修复此问题;仅在我们从递归目录返回后才进行此匹配检查。

\n
\n\n

结果是:

\n\n
\n

clean: 消除定义的歧义-d

\n\n

-d标志早于git-clean\ 指定路径的能力。
因此,默认情况下git-clean仅删除当前目录中未跟踪的文件,并-d允许其递归到子目录。

\n\n

路径和选项的交互-d似乎没有经过仔细考虑,大量错误和缺乏涵盖测试套件中此类配对的测试就证明了这一点。
\n 事实证明这个定义很重要,所以让我们看看可以解释该-d选项的一些不同方式:

\n\n

A) 不带-d,仅查找其下包含跟踪文件的子目录;使用-d,还可以在未跟踪的子目录中查找要清理的文件。

\n\n

B)如果没有用户指定的路径供我们删除,我们需要某种默认值,所以...without -d,只查找包含跟踪文件的子目录;使用-d,还可以在未跟踪的子目录中查找要清理的文件。

\n\n

-d这里重要的区别是,选项 B 表示如果指定了路径,则存在或不存在 \' \' 是无关紧要的。
选项 B 背后的逻辑是,如果用户明确要求我们清理指定的路径规范,那么我们应该清理与该路径规范匹配的任何内容。

\n\n

一些例子可能会澄清。

\n\n

应该:

\n\n
git clean -f untracked_dir/file\n
Run Code Online (Sandbox Code Playgroud)\n\n

是否删除 untracked_dir/file?
\n 不这样做似乎很疯狂,但严格阅读选项 A 表明它不应该被删除。
\n 怎么样:

\n\n
git clean -f untracked_dir/file1 tracked_dir/file2\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者

\n\n
git clean -f untracked_dir_1/file1 untracked_dir_2/file2\n
Run Code Online (Sandbox Code Playgroud)\n\n


\n 是否应该删除其中一个或两个文件?
\n 是否需要多次运行才能删除列出的两个文件?(如果这听起来像是一个疯狂的问题,请参阅此补丁系列前面添加的“t7300:添加一些\n测试用例,显示无法清理指定的路径规范”的提交消息。)
\n如果-ffd使用而不是-f——这是否应该允许这些被删除?是否应该多次调用-ffd
\n 如果使用 glob(例如“ tracked ”)而不是拼出目录名称会怎样?
\n 如果文件名涉及全局变量怎么办,例如

\n\n
git clean -f \'*.o\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者

\n\n
git clean -f \'*/*.o\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

\n\n

当前文档实际上建议的定义与 choice 略有不同A,并且本系列之前的实现提供了与选择 A 或 B 完全不同的东西。
(不过,该实现显然只是有缺陷)。

\n\n

可能还有其他选择。\n 然而,对于我能想到的
几乎任何给定的定义选择,上面的一些示例对用户来说都会出现错误。\n 唯一不会产生负面意外的情况是选择 B:将用户指定的路径视为清除与该路径规范匹配的所有未跟踪文件的请求,包括递归到任何未跟踪的目录。-d

\n\n

更改文档和基本实现以使用此定义。

\n\n

有两个回归测试间接依赖于当前的实现,但都不是关于子目录处理的。\n 这两个测试是在提交5b7570c
中引入的(“ :添加相对路径的测试”,2008-03-07,Git v1.5.5-rc0),它的创建只是为了添加对提交 fb328947c8e 中更改的覆盖范围(“git- clean:正确打印相对路径”,2008-03-07)。\n 两个测试都指定了一个恰好具有未跟踪子目录的目录,但两个测试都只是检查已删除文件的结果打印输出是否以相对路径显示。\n 适当更新这些测试。git-clean

\n
\n\n

最后,请参阅“ Git clean 排除嵌套子目录”。

\n\n
\n\n

警告:目录遍历代码具有冗余递归调用,这使得其性能特征相对于树的深度呈指数级增长,这一问题已在 Git 2.27(2020 年第 2 季度)中得到纠正。

\n\n

这会影响git clean

\n\n

请参阅提交 c0af173提交 95c11ec提交 7f45ab2提交 1684644提交 8d92fb2提交 2df179d提交 0126d14 、提交 cd129ee、提交446f46d提交 7260c7b提交 ce5c61a(2020 年 4 月 1 日)作者:伊利亚·纽伦( Elijah Newren newren)
\n请参阅Derrick Stolee ( )的提交 0bbd0e8(2020 年 4 月 1 日)。\n (由Junio C Hamano 合并 -- --提交 6eacc39,2020 年 4 月 29 日)derrickstolee
gitster

\n\n
\n

dir:用线性算法替换指数算法

\n\n

签署人:伊利亚·纽伦

\n\n

dir\read_directory_recursive()自然会递归操作以遍历目录树。

\n\n

目录的处理有时很奇怪,因为关于如何处理目录有很多不同的排列。

\n\n

一些例子:

\n\n
    \n
  • \' git ls-files -o --directory\' 只需要知道一个目录本身是未跟踪的;它不需要递归到它来查看下面的内容。
  • \n
  • \' git status\' 需要递归到一个未跟踪的目录,但只是为了确定它是否为空。
    \n 如果下面没有文件,目录本身将从输出中省略。
    \n 如果不为空,则仅列出目录。
  • \n
  • \' git status --ignored\' 需要递归到未跟踪的目录并报告所有被忽略的条目,然后将该目录报告为未跟踪 - 除非该目录下的所有条目都被忽略,在这种情况下我们不会打印该目录下的任何条目目录并仅将目录本身报告为被忽略。
    \n (请注意,虽然这迫使我们也遍历目录下的所有未跟踪文件,但我们将它们从输出中删除,除了像 \'git clean\' 这样也设置了 的用户DIR_KEEP_TRACKED_CONTENTS。)
  • \n
  • 对于“ git clean\”,如果目录下可能存在可以匹配其中一个路径规范的条目,我们可能需要递归到一个不匹配任何指定路径规范的目录。
    \n 在这种情况下,我们需要小心地从路径列表中省略目录本身(请参阅提交 404ebceda01c(“ dir:还检查目录是否匹配路径规范”,2019-09-17,Git v2.24.0-rc0) )
  • \n
\n\n

上面提到的部分紧张是目录的处理可以根据其中的文件以及dir->flags.

\n\n

在阅读代码时尝试牢记这一点,很容易想到“treat_directory()告诉我们如何处理目录,并且read_directory_recursive()是递归的事情”。

\n\n

不过,由于我们需要查看目录以了解如何处理它,因此很容易决定通过treat_directory()添加read_directory_recursive()调用来递归到该目录。

\n\n

添加这样的调用实际上很好,如果我们确保它read_directory_recursive()不会递归到同一目录中。

\n\n

不幸的是,commit df5bcdf83aeb(“ dir:递归到未跟踪的目录中以获取被忽略的文件”,2017-05-18,Git v2.14.0-rc0 -批量#5中列出的合并),在代码中添加了这样的情况,这意味着我们\会对未跟踪的目录进行两次调用。 read_directory_recursive()

\n\n

所以,如果我们有一个名为

\n\n
one/two/three/four/five/somefile.txt\n
Run Code Online (Sandbox Code Playgroud)\n\n

并且没有one/跟踪到任何内容,那么“\ ”将在目录“\”上git status --ignored调用两次,并且每个目录将在目录“ \”上调用两次,依此类推,直到“\”被调用多次。read_directory_recursive()one/read_directory_recursive()one/two/read_directory_recursive()2^5one/two/three/four/five/

\n\n

通过将read_directory_recursive()大量特殊逻辑移至treat_directory().

\n\n

由于dir.c有点复杂,随着时间的推移,会产生额外的麻烦。

\n\n

在试图解开它时,我注意到有几个实例,其中第一次调用read_directory_recursive()将返回例如 \n path_untracked某个目录,而稍后的调用将返回例如 \n path_none,,尽管该目录显然应该被视为未跟踪。

\n\n

该代码之所以能够工作,是因为第一次调用添加未跟踪条目的副作用dir->entries;使其能够获得正确的输出,尽管稍后的调用在返回值中假设覆盖了该代码。

\n\n

我有点担心仍然存在错误,甚至可能是带有错误期望的测试用例。

\n\n

我尝试仔细记录,treat_directory()因为在这次更改之后它变得更加复杂(尽管这种复杂性大部分来自其他地方,可能值得更好的评论)。

\n\n

然而,我的大部分工作感觉更像是一场 whackamole 游戏,同时试图使代码与现有的回归测试相匹配,而不是尝试创建与某些清晰设计相匹配的实现。

\n\n

这对我来说似乎是错误的,但现有行为规则有如此多的特殊情况,以至于我很难想出一些关于所有情况下正确行为的总体规则,迫使我希望回归测试是正确和充分的。

\n\n

dir.c鉴于我在过去几个月中相关测试用例的经验,这种希望似乎是没有根据的:

\n\n

文档难以解析甚至错误的示例:

\n\n
    \n
  • 3aca58045f4fgit-clean.txt:不要声称我们将删除带有-n/ 的文件--dry-run,2019-09-17,Git v2.24.0-rc0)
  • \n
  • 09487f2cbad3clean:避免删除嵌套 git\n 存储库中未跟踪的文件,2019-09-17,v2.24.0-rc0)
  • \n
  • e86bbcf987faclean:消除定义的歧义-d,2019-09-17)
  • \n
\n\n

测试用例被声明错误并更改的示例:

\n\n
    \n
  • 09487f2cbad3clean:避免删除嵌套 git 存储库中未跟踪的文件,2019-09-17,Git v2.24.0-rc0)
  • \n