我对不想提交到存储库的文件进行了本地更改。它是用于在服务器上构建应用程序的配置文件,但我想使用不同的设置在本地构建。自然,当我将“git status”作为要暂存的内容时,该文件总是会显示出来。我想隐藏这个特定的更改而不是提交它。我不会对文件进行任何其他更改。
澄清一下,使用 .gitignore 不是我想要的,因为这只会阻止添加新文件。我想忽略对存储库中已有文件的更改。
经过一番挖掘,我看到了 2 个选项:assume-unchanged
和 skip-worktree
。上一个问题 here 谈到了它们,但并没有真正解释它们的区别。
这两个命令有何不同?为什么有人会使用其中一个?
你想要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 正是您想要的。
注意: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 7a2dc95、commit 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。
assume-unchanged
而不是 skip-worktree
的类似消息。
assume-changed
和skip-worktree
对于 git pull
或 rebase
,两者都会抛出 please commit or stash 即使 git status
什么也不显示
使用 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
--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
。详情heregit update-index --skip-worktree <file_name>