为什么我不能丢弃由 `git clean` 过滤器引起的本地更改?

Why can't I discard local changes caused by a `git clean` filter?

我的存储库中有一些 .resx files 包含我的应用程序的字符串翻译。这很好用,除了在单独的 git 分支中将新字符串添加到文件末尾时发生合并冲突。 KDiff3 在合并 XML 对列表时表现不佳。

resx 文件基本上是 key/value-pairs 的列表,没有特定的顺序。为了避免合并冲突,因此我想在提交之前按字母顺序对这些对进行排序,并且我已经使用优秀的 SortRESX program 使用 git 过滤器来做到这一点:

git config --global filter.resx.clean SortRESX
git config --global filter.resx.smudge cat

哪个做的工作。但是,如果我签出一个未排序的文件,它会立即被过滤器排序,并且 git 不允许我放弃这些更改——我不得不在切换分支之前提交文件的排序版本。如何在不提交的情况下放弃过滤器所做的更改?

However, if I check out an unsorted file it will be sorted immediately by the filter

这不应该发生(我认为!)。我相信其他事情实际上正在发生。

clean 过滤器的 intent/idea 是在 将文件添加到索引时应用它 ,并应用涂抹过滤器 当文件从索引中提取到工作树中时(这就是为什么git checkout必须首先将提交的文件写入索引,然后再将其复制到工作树中-树,当你使用 git checkout <commit> -- <path> 时)。请注意,行尾/CRLF 转换被视为一种过滤器形式(如果可能,在内部完成,但如果需要,则在来自或到您实际用户提供的过滤器的管道上完成)。

(有可能我遗漏了一些代码,在某些额外情况下 运行s clean 过滤器。但我不这么认为:[=80 的这一部分=] 来源相当明显。)

我相信正在发生的事情更加微妙。当 Git 应用污迹过滤器时,它 会自动在索引 中标记缓存条目 "dirty"。 (这段代码不太明显,所以我在这里可能是错的。)由于这个标记,当 Git 转到 检查 文件的状态时,它说: 嗯,这个缓存条目被标记为脏的,我最好运行对其进行clean过滤并确定找出答案。所以它运行s您的 clean 过滤器对 key/value 对进行排序,然后将结果与基础 blob 进行比较。这些不同,因此 Git 现在声明工作树条目 "truly dirty", 即使原始的、未排序的工作树条目实际上匹配当前提交 .

换句话说,Git 假定 git cat-file <hash-id> | smudge | clean 的等价物产生 git cat-file <hash-id> 相同的 位,如果它不产生' t,你应该提交文件——当你试图规范化存储库中存储的行结尾时,这实际上通常是正确的。这并不意味着签出的副本已排序;您的 cat 过滤器(顺便说一句,您可以丢弃:不存在的过滤器意味着 "leave this alone")没有对文件进行排序,并且工作树副本仍未排序。只是 Git 坚持认为它应该 变成 排序。

这到底是什么意思,答案是:

How can I discard the changes made by the filter without committing?

就是简单地忽略Git的抱怨,无论如何检查其他提交。不过,您可能必须使用 --force 标志来执行此操作,这充其量是令人不安的(最坏的情况是,可能会导致您丢失原本打算保留的更改!)。所以有一个稍微好一点的方法:暂时禁用 "clean" 过滤器(通过编辑 .gitattributes)。

禁用过滤器(或替换为 cat,它做同样的事情只是速度较慢),Git 现在将在检查状态时看到 "dirty" 标志是设置并重复 Hmm,我最好 运行 cleanfilter 东西。这次过滤器是空操作,生成的二进制位与 blob 匹配,并且 Git 清除脏标志。您现在可以随时恢复过滤器,因为现在缓存条目不再标记为脏,并且 Git 将跳过所有这些测试。

(在声明文件 "truly dirty" 之前,有办法让 Git 尝试 两次 比较可能会更好:一个使用实际的,配置 clean 过滤器,然后如果显示 "dirty",使用 no 过滤器一次 更多 次。这将自动决定基于 "uncleaned" in-repo blob 的工作树文件,但无论如何最终都匹配该 blob,实际上是 "not dirty"。当然,这意味着我们不会鼓励您修复行尾,但如果这是一个用户定义的开关,您可以为包含不干净对象的旧存储库设置它,就像为此类存储库设置 merge.renormalize 一样。)