我对一个文件进行了本地更改,但不想提交到存储库中。这是一个用于在服务器上构建应用程序的配置文件,但我想在本地使用不同的设置构建应用程序。当然,文件总是显示当我做'git状态'作为要上演的东西。我希望隐藏这个特定的更改,而不提交它。我不会对该文件做任何其他更改。

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

经过一番挖掘,我看到了2个选项:假设-不变和跳过-工作树。之前的一个问题谈到了它们,但并没有真正解释它们的区别。

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


当前回答

你需要跳跃式工作树。

假设-unchanged是为检查一组文件是否已被修改的代价昂贵的情况而设计的;当您设置位时,git(当然)假定索引的那一部分对应的文件在工作副本中没有被修改。这样就避免了数据调用的混乱。当索引中的文件条目发生变化时(因此,当文件在上游发生变化时),这个位就会丢失。

Skip-worktree不仅如此:即使git知道文件已经被修改过(或者需要通过重置来修改——很难或者类似的),它也会假装它没有被修改过,而是使用索引中的版本。这一直持续到索引被丢弃为止。

这里有一个关于这种差异的分支和典型用例的很好的总结:http://fallengamer.livejournal.com/93321.html。

从那篇文章中:

——assume-unchanged假设开发人员不应该更改文件。这个标志是为了提高像sdk这样的不可更改文件夹的性能。 ——skip-worktree是有用的,当你指示git不要碰一个特定的文件,因为开发人员应该改变它。例如,如果主存储库上游托管了一些可用于生产的配置文件,而您不想意外地向这些文件提交更改,那么——skip-worktree正是您想要的。

其他回答

你需要跳跃式工作树。

假设-unchanged是为检查一组文件是否已被修改的代价昂贵的情况而设计的;当您设置位时,git(当然)假定索引的那一部分对应的文件在工作副本中没有被修改。这样就避免了数据调用的混乱。当索引中的文件条目发生变化时(因此,当文件在上游发生变化时),这个位就会丢失。

Skip-worktree不仅如此:即使git知道文件已经被修改过(或者需要通过重置来修改——很难或者类似的),它也会假装它没有被修改过,而是使用索引中的版本。这一直持续到索引被丢弃为止。

这里有一个关于这种差异的分支和典型用例的很好的总结:http://fallengamer.livejournal.com/93321.html。

从那篇文章中:

——assume-unchanged假设开发人员不应该更改文件。这个标志是为了提高像sdk这样的不可更改文件夹的性能。 ——skip-worktree是有用的,当你指示git不要碰一个特定的文件,因为开发人员应该改变它。例如,如果主存储库上游托管了一些可用于生产的配置文件,而您不想意外地向这些文件提交更改,那么——skip-worktree正是您想要的。

注:fallengamer在2011年做了一些测试(所以它们可能已经过时了),以下是他的发现:

操作

文件在本地存储库和上游都被更改 git拉: Git无论如何都会保留本地更改。 因此,您不会意外地丢失使用任何标志标记的任何数据。 带有assume-unchanged标志的文件:Git不会覆盖本地文件。相反,它将输出冲突和如何解决冲突的建议 带有skip-worktree标志的文件:Git不会覆盖本地文件。相反,它将输出冲突和如何解决冲突的建议

File is changed both in local repository and upstream, trying to pull anyway git stash git pull Using skip-worktree results in some extra manual work but at least you wouldn’t lose any data if you had any local changes. File with assume-unchanged flag: Discards all local changes without any possibility to restore them. The effect is like ‘git reset --hard’. ‘git pull’ call will succeed File with skip-worktree flag: Stash wouldn’t work on skip-worktree files. ‘git pull’ will fail with the same error as above. Developer is forced to manually reset skip-worktree flag to be able to stash and complete the failing pull.

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

修改了本地文件 Git重置——很难 Git不涉及跳跃式工作树文件,并反映了假设不变文件的现实(承诺不变的文件实际上已被更改)。 带有assume-unchanged标志的文件:恢复文件内容。标志被重置为H(从H)。 带skip-worktree标志的文件:文件内容完整。旗帜保持不变。

他补充了以下分析:

看起来skip-worktree正在非常努力地保存本地数据。但如果安全的话,它不会阻止您进行上游更改。而且少不更事者不会在拉扯时重置标志。 但忽略“reset—hard”命令可能会让开发人员大吃一惊。 假设-unchanged标志可能会在pull操作中丢失,而这些文件中的本地更改对git来说似乎并不重要。

See:

Junio's (current git maintainer) comment regarding intent of assume-unchanged, In particular, Junio points out that changes to assume-unchanged files could accidentally be committed: "if Git can determine a path that is marked as assume-unchanged has changed without incurring extra lstat(2) cost, it reserves the right to report that the path has been modified (as a result, git commit -a is free to commit that change)." difference between assume-unchanged and skip-worktree as discussed in git mailing list upon addition of skip-worktree patch.

他的结论是:

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

assume-unchanged assumes that a developer shouldn’t change a file. If a file was changed – then that change is not important. This flag is meant for improving performance for not-changing folders like SDKs. But if the promise is broken and a file is actually changed, git reverts the flag to reflect the reality. Probably it’s ok to have some inconsistent flags in generally not-meant-to-be-changed folders. On the other hand skip-worktree is useful when you instruct git not to touch a specific file ever. That is useful for an already tracked config file. Upstream main repository hosts some production-ready config but you would like to change some settings in the config to be able to do some local testing. And you don’t want to accidentally check the changes in such file to affect the production config. In that case skip-worktree makes perfect scene.


在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邮件列表)

doc: dissuade users from trying to ignore tracked files Signed-off-by: Jeff King Signed-off-by: brian m. carlson It is quite common for users to want to ignore the changes to a file that Git tracks. Common scenarios for this case are IDE settings and configuration files, which should generally not be tracked and possibly generated from tracked files using a templating mechanism. However, users learn about the assume-unchanged and skip-worktree bits and try to use them to do this anyway. This is problematic, because when these bits are set, many operations behave as the user expects, but they usually do not help when git checkout needs to replace a file. There is no sensible behavior in this case, because sometimes the data is precious, such as certain configuration files, and sometimes it is irrelevant data that the user would be happy to discard. Since this is not a supported configuration and users are prone to misuse the existing features for unintended purposes, causing general sadness and confusion, let's document the existing behavior and the pitfalls in the documentation for git update-index so that users know they should explore alternate solutions. In addition, let's provide a recommended solution to dealing with the common case of configuration files, since there are well-known approaches used successfully in many environments.

git更新索引手册页现在包括:

Users often try to use the assume-unchanged and skip-worktree bits to tell Git to ignore changes to files that are tracked. This does not work as expected, since Git may still check working tree files against the index when performing certain operations. In general, Git does not provide a way to ignore changes to tracked files, so alternate solutions are recommended. For example, if the file you want to change is some sort of config file, the repository can include a sample config file that can then be copied into the ignored name and modified. The repository can even include a script to treat the sample file as a template, modifying and copying it automatically.

最后一部分是我描述的基于smudge/clean脚本的典型内容过滤器驱动程序。

使用跳跃式工作树如下:

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