ChatGPT解决这个技术问题 Extra ChatGPT

Git - “假设不变”和“跳过工作树”之间的区别

我对不想提交到存储库的文件进行了本地更改。它是用于在服务器上构建应用程序的配置文件,但我想使用不同的设置在本地构建。自然,当我将“git status”作为要暂存的内容时,该文件总是会显示出来。我想隐藏这个特定的更改而不是提交它。我不会对文件进行任何其他更改。

澄清一下,使用 .gitignore 不是我想要的,因为这只会阻止添加新文件。我想忽略对存储库中已有文件的更改。

经过一番挖掘,我看到了 2 个选项:assume-unchangedskip-worktree。上一个问题 here 谈到了它们,但并没有真正解释它们的区别。

这两个命令有何不同?为什么有人会使用其中一个?

但是不能像这里解释的那样删除所有并添加所有以便“刷新”吗? stackoverflow.com/questions/7075923/…@Grigory
如果我正确理解了 OP 的意图,则不应忽略该文件。该文件必须在存储库中,但不应该提交他所做的这些非常具体的更改——至少现在不应该。
Google 员工:see also 我将接受 Junio Hamano(Git 的维护者)的电子邮件回复,因为我认为它比官方文档更清楚地解释了一些事情,并且可以视为“官方”建议
2022 年第一季度正在进行有趣的补丁:public-inbox.org/git/20220109045732.2497526-4-newren@gmail.com/…

B
Borealid

你想要skip-worktree

assume-unchanged 设计用于检查一组文件是否已被修改的成本很高的情况;当您设置该位时,git(当然)假定与索引的该部分对应的文件在工作副本中没有被修改。所以它避免了 stat 调用的混乱。每当索引中的文件条目发生更改时(因此,当文件在上游更改时),该位都会丢失。

skip-worktree 不止于此:即使 git 知道文件已被修改(或需要由 reset --hard 等修改),它也会假装它没有被修改,而是使用索引中的版本。这一直持续到索引被丢弃。

此处对这种差异的后果和典型用例进行了很好的总结:http://fallengamer.livejournal.com/93321.html

从那篇文章:

--assume-unchanged 假定开发人员不应该更改文件。此标志旨在提高 SDK 等不更改文件夹的性能。

--skip-worktree 当您指示 git 不要碰特定文件时很有用,因为开发人员应该更改它。例如,如果上游的主存储库托管了一些生产就绪的配置文件,并且您不想意外提交对这些文件的更改,那么 --skip-worktree 正是您想要的。


一个小笔记可以节省几秒钟的搜索和阅读时间。要取消 --skip-worktree 效果并取消设置标志,有 --no-skip-worktree 选项。工作方式完全相同。这在手滑并标记了错误文件的情况下很有用,或者如果情况发生了变化并且不应再忽略以前跳过的文件。
要回答我上面自己的问题,使用 --skip-worktree.git/info/exclude 文件之间的区别在于前者甚至适用于当前跟踪的文件。 .git/info/exclude.gitignore 一样,只会防止意外将未跟踪的文件添加到索引中,但不会对已跟踪的文件进行更改。
这可以推送到远程并由所有克隆保存吗?
不幸的是:--skip-worktree--assume-unchanged 都不允许切换到不同的分支。我收到错误:Your local changes to the following files would be overwritten by checkout。详情here
只是the usage,女士:git update-index --skip-worktree <file_name>
M
Michael

注意:fallengamer 在 2011 年进行了一些测试(因此它们可能已经过时),以下是他的 findings

运营

本地存储库和上游 git pull 中的文件都已更改:Git 无论如何都会保留本地更改。因此,您不会意外丢失用任何标志标记的任何数据。带有假设不变标志的文件:Git 不会覆盖本地文件。相反,它会输出冲突并建议如何解决它们 带有 skip-worktree 标志的文件:Git 不会覆盖本地文件。相反,它会输出冲突和建议如何解决它们

带有假设不变标志的文件:Git 不会覆盖本地文件。相反,它会输出冲突和建议如何解决它们

带有 skip-worktree 标志的文件:Git 不会覆盖本地文件。相反,它会输出冲突和建议如何解决它们

文件在本地存储库和上游都发生了更改,无论如何都试图拉动 git stash git pull 使用 skip-worktree 会导致一些额外的手动工作,但至少如果您有任何本地更改,您不会丢失任何数据。带有假设未更改标志的文件:丢弃所有本地更改,而无法恢复它们。效果就像'git reset --hard'。 'git pull' 调用将成功 带有 skip-worktree 标志的文件:Stash 不适用于 skip-worktree 文件。 'git pull' 将失败并出现与上述相同的错误。开发人员被迫手动重置 skip-worktree 标志,以便能够存储并完成失败的拉取。

带有假设未更改标志的文件:丢弃所有本地更改,而无法恢复它们。效果就像'git reset --hard'。 'git pull' 调用将成功

带有skip-worktree 标志的文件:Stash 不适用于skip-worktree 文件。 'git pull' 将失败并出现与上述相同的错误。开发人员被迫手动重置 skip-worktree 标志,以便能够存储并完成失败的拉取。

没有本地更改,上游文件更改 git pull 两个标志都不会阻止您获取上游更改。 Git 检测到您违反了假设不变的承诺,并选择通过重置标志来反映现实。带有假设不变标志的文件:内容已更新,标志丢失。 'git ls-files -v' 将显示该标志已修改为 H(来自 h)。带有 skip-worktree 标志的文件:更新内容,保留标志。 'git ls-files -v' 将显示与拉取之前相同的 S 标志。

带有假设不变标志的文件:内容已更新,标志丢失。 'git ls-files -v' 将显示该标志已修改为 H(来自 h)。

带有 skip-worktree 标志的文件:更新内容,保留标志。 'git ls-files -v' 将显示与拉取之前相同的 S 标志。

随着本地文件更改 git reset --hard Git 不会触及跳过工作树文件并反映假设未更改文件的现实(承诺不变的文件实际上已更改)。带有假定未更改标志的文件:文件内容已还原。标志重置为 H(从 h)。带有 skip-worktree 标志的文件:文件内容完好无损。标志保持不变。

带有假定未更改标志的文件:文件内容已还原。标志重置为 H(从 h)。

带有 skip-worktree 标志的文件:文件内容完好无损。标志保持不变。

他补充了以下分析:

看起来 skip-worktree 正在努力保存您的本地数据。但是,如果它是安全的,它不会阻止您获取上游更改。另外 git 不会在 pull 时重置标志。但是忽略“reset --hard”命令可能会令开发人员大吃一惊。

Assume-unchanged 标志可能会在拉取操作中丢失,并且此类文件中的本地更改似乎对 git 并不重要。

看:

Junio(当前 git 维护者)关于假设不变的意图的评论,特别是,Junio 指出对假设不变文件的更改可能会意外提交:“如果 Git 可以确定标记为假设不变的路径已更改而不会发生额外的 lstat(2) 成本,它保留报告路径已被修改的权利(因此, git commit -a 可以自由提交该更改)。

在添加跳过工作树补丁后,假设未更改和跳过工作树之间的区别在 git 邮件列表中讨论。

他总结道:

实际上,这两个标志都不够直观。

假定未更改假定开发人员不应该更改文件。如果一个文件被改变了——那么这个改变并不重要。此标志旨在提高 SDK 等不更改文件夹的性能。但是如果承诺被破坏并且文件实际上被更改,git 会恢复标志以反映现实。可能在通常不打算更改的文件夹中有一些不一致的标志是可以的。

另一方面,当您指示 git 永远不要触摸特定文件时,skip-worktree 很有用。这对于已经跟踪的配置文件很有用。上游主存储库托管一些生产就绪配置,但您希望更改配置中的一些设置以便能够进行一些本地测试。并且您不想意外检查此类文件中的更改以影响生产配置。在这种情况下,skip-worktree 是完美的场景。

使用 Git 2.25.1(2020 年 2 月),进一步澄清了上面提到的“实际上这两个标志都不够直观”:

请参阅 brian m. carlson (bk2204)commit 7a2dc95commit 1b13e90(2020 年 1 月 22 日)。
(由 Junio C Hamano -- gitster --commit 53a8329 中合并,2020 年 1 月 30 日)
Git Mailing list )

文档:劝阻用户不要尝试忽略跟踪的文件 署名:Jeff King 署名:brian m。 carlson 用户想要忽略 Git 跟踪的文件的更改是很常见的。这种情况的常见场景是 IDE 设置和配置文件,它们通常不应该被跟踪,并且可能使用模板机制从跟踪的文件中生成。但是,用户了解了假设不变和跳过工作树位并尝试使用它们来执行此操作。这是有问题的,因为当设置了这些位时,许多操作会按照用户的预期进行,但是当 git checkout 需要替换文件时它们通常无济于事。在这种情况下没有明智的行为,因为有时数据很宝贵,例如某些配置文件,有时用户会很乐意丢弃不相关的数据。由于这不是受支持的配置,并且用户很容易将现有功能误用于非预期目的,从而导致普遍的悲伤和困惑,让我们在 git update-index 文档中记录现有行为和陷阱,以便用户知道他们应该探索替代方案解决方案。此外,让我们提供一个推荐的解决方案来处理配置文件的常见情况,因为在许多环境中成功使用了众所周知的方法。

git update-index man page 现在包括:

用户经常尝试使用假设不变和跳过工作树位来告诉 Git 忽略对跟踪文件的更改。这不像预期的那样工作,因为 Git 在执行某些操作时可能仍会根据索引检查工作树文件。一般来说,Git 不提供忽略跟踪文件更改的方法,因此建议使用替代解决方案。例如,如果您要更改的文件是某种配置文件,则存储库可以包含一个示例配置文件,然后可以将其复制到忽略的名称中并进行修改。存储库甚至可以包含一个脚本,将示例文件视为模板,自动修改和复制它。

最后一部分是我描述的典型 content filter driver based on smudge/clean scripts


如果您在文件上有 skip-worktree 并且上游发生更改,那么当您尝试拉取时,您会得到“请提交或存储”,即使 git status 没有报告文件已更改。你怎么能避免这种情况,所以当人们在原产地设置生产设置时,本地更改可以持续存在?
@GreenAsJade 不确定:这本身就是一个很好的问题(带有指向此问题的链接)。我想您会收到使用 assume-unchanged 而不是 skip-worktree 的类似消息。
是的,我可以确认你这样做。这意味着仍然很难拥有一个您只想以不同于原始文件的方式维护的本地文件。
@GreenAsJade 似乎很古老。你有机会用 2.2.x 测试它吗?
@VonC 如何解决 assume-changedskip-worktree 对于 git pullrebase,两者都会抛出 please commit or stash 即使 git status 什么也不显示
f
fritz

使用 skip-worktree,如下所示:

git update-index --skip-worktree changedfile.txt

确认文件状态:

 git ls-files -v
 S changedfile.txt

第一个字母 S 表示文件被标记为 skip-worktree 选项

原因:

“git-update-index --assume-unchanged 绝不是要忽略对跟踪文件的更改(只是为了节省一些统计数据)。所以不要建议将其作为实现这一目标的手段。”

见:https://github.com/git/git/commit/936d2c9301e41a84a374b98f92777e00d321a2ea

--skip-worktree 解释:

此选项忽略对已跟踪文件的更改。 git 始终使用索引中的文件内容和属性,无论对工作目录中的文件进行任何修改。这允许您对不想被推送到上游的文件进行更改。如上所示使用此选项。

要取消设置此选项,请使用 --no-skip-worktree,如下所示:

git update-index --no-skip-worktree changedfile.txt

--assume-unchanged 解释:

此选项专门用于检查某些文件是否已被修改的资源密集型用例。例如,为了优化慢速文件系统上的资源使用,git 不会检查文件以查看有问题的文件是否已更改,它假定工作目录中的文件没有被修改。每当索引中的文件条目发生更改时,此标志就会丢失。例如,当文件被上游修改时。

它的用法如下:

git update-index --assume-unchanged changedfile.txt

确认文件状态:

git ls-files -v
h changedfile.txt

第一个字母 h 表示文件标记有假定不变的选项 要取消设置此选项,请使用 –no-assume-unchanged,如下所示:

git update-index --no-assume-unchanged changedfile.txt