【问题标题】:Stop git from silently clobbering ignorefiles (bug in git?)?阻止 git 默默地破坏忽略文件(git 中的错误?)?
【发布时间】:2025-12-22 09:25:09
【问题描述】:

我找到了一种方法,git 不会要求您存储,而是默默地删除您认为在 .gitignore 中安全的文件。 即使您自初始提交以来拥有相同的忽略文件也是如此。

当您处于已删除但在 .gitignore 中列出的上述文件的提交中,然后您签出另一个存在的提交时,就会出现问题:

git init
mkdir ignored/
echo stuff > ignored/file
echo otherstuff > otherfile
git add -A
# Opps added my ignored folders files
# Forgeting to rm --cache
echo ignored/ > .gitignore ; git add .gitignore
git commit -m 'accidentally add ignored/file'
git status
touch dummyfile; git add dummyfile
# Remembered to rm --cache
git rm --cache -rf ignored/file #This file is now vulnerable to clobber
git commit -m 'add stuff'
echo somechange >> ignored/file

## Wait for it..
git checkout HEAD~
## somechange has been silently clobbered!!

# Please paste first paragraph, observe and then past the second.
# Note both commits have the correct ignore file and are not immune!

(在将上述代码粘贴到终端之前,cd 到一个空文件夹)

有没有办法阻止这种无声的破坏?

【问题讨论】:

    标签: git


    【解决方案1】:

    这不是一个错误(或者至少,git 开发人员不认为它是一个错误)。

    .gitignore的内容不是“应该被忽略的文件”或者“不应该被触及的路径名”;相反,它们是“不会自动添加的路径,并且会被禁止显示为未跟踪”(这使得 .gitignore 成为一个糟糕的名字)。

    一旦您提交了一个文件,甚至将其添加到索引中,它就不再被忽略,无论它是否列在 .gitignore 中。

    对于某些文件,您可以使用git update-index --assume-unchanged 或(更好)git update-index --skip-worktree,但一般来说,如果您不小心提交了一个您不应该拥有的文件,并且您希望它被忽略,您必须“重写历史记录”将其完全从存储库中取出以获得良好的行为。如果您没有推送任何内容并且很少有包含不需要的文件的提交,这并不太难,但如果您已经推送或有很多此类提交,那就更难了。

    另见Git - Difference Between 'assume-unchanged' and 'skip-worktree'

    [以下文字于 2016 年 12 月添加]

    技术细节

    对于 Git,已跟踪 文件有一个简短、简单且实用的定义:当且仅当索引中有一个文件时,该文件才会被跟踪。

    “假设不变”和“跳过工作树”是您可以在索引中手动设置或清除的标志。它们是单独的标志,可以单独设置,尽管不清楚设置两者意味着什么。 “假设不变”的 intent 只是为了让 Git 更快,允许它假设文件没有改变,因此不需要由 @987654329 更新@,而“跳过工作树”标志的 intent 是告诉 Git “放手”:不要只是 假设它没有改变,努力保持 它没有改变。这比乍看起来要难。有关这方面的更多信息,请参阅上面的linked post

    为了设置这些标志,文件必须有一个索引条目,所以它必须被跟踪。

    如果一个文件没有被跟踪,它可能会也可能不会被忽略。这些来源有很多:不仅是* .gitignore,它包含 Git 的 pathspec 变体的列表(每行一个),该变体仅限于 glob 匹配和否定,而且.gitignore 存储库中每个子目录中的文件,$GIT_DIR/info/exclude 文件(如果存在),以及由core.excludesFile 命名的文件(如果该配置条目存在)。要查找是否忽略文件以及忽略文件的原因,请使用 git check-ignore,自 Git 版本 1.8.2 起可用。 -v 选项告诉您哪个控制文件将文件标记为忽略。

    不幸的是,在这个问题的答案中,“忽略”有两个的含义。虽然匹配忽略路径使 Git 对未跟踪的文件保持安静,但它让 Git 可以随意破坏文件。

    【讨论】:

    • 糟糕,我想。所以基本上它是.git<feelFreeToClobberThese>ignore,当你签出提交时,通常 git floats 会遇到可怕的事情。很多人都依赖这个(而且不应该)。
    • 是的,确实应该有某种方法至少让 git 对此发出警告。不过,目前还不清楚这种方式应该是什么。任何被记录为“存在于 rev X 中,但不在 rev Y 中”的 other 文件应在从 rev X 到 rev Y 时被静默删除。也许是一个新的指令文件,例如.gitprecious , 可以创建,并且可以在此处列出不应删除的文件。这使git checkout 的工作变得复杂,它现在必须首先提取目标提交的“重要列表”,然后决定哪些文件可以被破坏,但它会提供一种救生衣。
    • --skip-worktree 不是要使用的东西吗?我认为 --assume-unchanged 应该是针对当前未更改文件的情况?
    • @sabgenton:我还没有尝试过,所以我真的不能说使用哪一个。有很多 git 邮件列表讨论(我记得大约从 2010 年开始)关于如何将被忽略的文件分为两类:git 不应该签入 随意破坏的文件,vs git 不应该签入的文件应该破坏(“珍贵”文件)。不过,它从未被正确实施。
    • "特别是 git rm 或 git rm --cached 会在索引中写入一个特殊的“待删除”条目," . . .我不认为它是这样工作的,也没有在代码中看到它,你能解释一下这是指什么吗?