阻止Git在静默地覆盖ignore文件时出现问题(Git的错误)?

10

我找到了一种方法,可以让git在不要求你stash的情况下,将你认为在.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!

(将上述代码粘贴到终端之前,请先转到空文件夹)

有没有什么办法可以防止这种静默覆盖?

1个回答

9

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

.gitignore 的内容并不是“应该被忽略的文件”或“不应该被操作的路径名”,而是“不会自动添加,并且被禁止显示为未跟踪的路径”(这使得 .gitignore 是一个不太合适的名称)。

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

对于某些文件,你可以使用 git update-index --assume-unchanged 或(更好的)git update-index --skip-worktree,但通常情况下,如果你意外地提交了一个不应该提交的文件,并且想要它被忽略,你必须“重写历史记录”以完全将其从仓库中移除以获得良好的行为。如果你还没有推送任何东西并且有很少的提交包含不想要的文件,则这并不太困难,但如果你已经推送了或有许多这样的提交,则会更加困难。

另请参阅Git-“assume-unchanged”和“skip-worktree”之间的区别

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

技术细节

对于 Git,有一个简短、简单而甜美的定义:跟踪文件:只有在索引中有其条目时,文件才被跟踪。

"假设未更改"和"跳过工作树"是您可以在索引中手动设置或清除的标志。它们是单独的标志,可以分别设置,尽管设置两者的含义不太清楚。 "假设未更改"的目的仅仅是使Git更快,通过允许它假设文件没有更改,因此不需要通过“git add”进行更新,而“跳过工作树”标志的目的是告诉Git“不要碰它”:不要仅仅假设它没有更改,而是尽力保持它不变。这比起初看起来要困难得多;有关此内容的更多信息,请参见上面的链接帖子
为了设置这些标志,文件必须具有索引条目,因此必须被跟踪。"
如果一个文件没有被跟踪,它可能会被忽略或不被忽略。有许多来源可以实现这一点:不仅是顶层的.gitignore,其中包含一个列表(每行一个)Git的pathspec变量,限于通配符匹配和否定,还有在存储库中每个子目录中的.gitignore文件,如果存在$GIT_DIR/info/exclude文件以及由core.excludesFile配置项指定的文件。要查找文件是否被忽略以及原因,请使用自从Git 1.8.2版本以来可用的git check-ignore命令。选项-v告诉您哪个控制文件将文件标记为已忽略。

不幸的是,正如这个问题的答案所述,“被忽略”有两个意思。虽然匹配忽略路径使Git不再提醒该文件未被跟踪,但它也使Git可以随意覆盖该文件。

哦,正如我所想。基本上它是.git<feelFreeToClobberThese>ignore,通常情况下,当您检出提交时,Git 浮动工作会跨越这个文件。很多人依赖它(而不应该)。 - sabgenton
是的,至少应该有一种方法让git警告这个问题。不过目前还不太清楚应该采用什么方式。任何其他被记录为“存在于版本X中但不存在于版本Y中”的文件都应该在从版本X切换到版本Y时被静默删除。也许可以创建一个新的指令文件,例如.gitprecious,并在其中列出不应该被删除的文件。这会使git checkout的工作变得复杂,它现在必须首先提取目标提交的“宝贵列表”,然后决定哪些文件可以被覆盖,但它将提供一种救生圈。 - torek
--skip-worktree 不是更适合使用吗?我认为 --assume-unchanged 应该是用于文件当前未更改的情况下。 - sabgenton
1
@sabgenton:我没有尝试过,所以我无法真正说出应该使用哪一个。有很多关于Git邮件列表的讨论(我记得是从2010年左右开始),关于忽略文件可能应该分为两类:Git不应检查并且可以随意覆盖的文件,以及Git不应检查但应该覆盖的“宝贵”文件。虽然这个功能从未被正确实现。 - torek
@jthill:现在已更新,我删除了多余的脚注。也许有一段时间CE_REMOVE条目被写回索引文件。当然我现在无法再现它,但除了比特本身之外,我还记得某种可观察行为表明索引条目仍然存在。 - torek
显示剩余3条评论

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