我能否在使用 git commit 命令时忽略指定文件的内容变更?

434

我的团队中每个开发人员都有自己的本地配置,这些配置存储在名为devtargets.rb的文件中,用于我们的rake构建任务。我不想让开发者互相覆盖他们的devtargets文件。

我的第一个想法是将该文件放入.gitignore列表中,以便不提交到git。

然后我开始想:是否可能提交文件,但忽略文件的更改?因此,我会提交一个默认版本的文件,然后当开发人员在他们的本地计算机上更改它时,git会忽略更改,并且在执行git status或git commit时不会显示在更改文件列表中。

这可行吗?这肯定是一个好功能...


1
请参考类似主题的 https://dev59.com/-nA75IYBdhLWcg3wZ4Fr。 - VonC
可能是提交机器特定配置文件的重复问题。 - Senseful
仅在本地忽略Git文件 - phuclv
5个回答

531

当然,我有时会使用以下方法来做到这一点

git update-index --assume-unchanged [<file> ...]

如果想撤销并重新开始跟踪(如果您忘记了哪些文件未被跟踪,请查看此问题):

git update-index --no-assume-unchanged [<file> ...]

相关文档:

--[no-]assume-unchanged
如果指定了此标志,则不会更新路径所记录的对象名称。相反,此选项为路径设置/取消“假定未更改”位。当“假定未更改”位打开时,用户承诺不更改文件,并允许Git假定工作树文件与索引中记录的内容匹配。如果您想更改工作树文件,则需要取消位以告诉Git。在使用具有非常缓慢的lstat(2)系统调用(例如cifs)的文件系统上处理大型项目时,这有时很有帮助。

如果需要修改索引中的此文件(例如,在合并提交时),Git将失败(优雅地);因此,如果上游更改了假定未跟踪文件,则需要手动处理该情况。

在这种情况下,优雅地失败意味着,如果在您进行拉取时对该文件进行任何更改(合法更改等),它将显示:

$ git pull

From https://github.com/x/y
   72a914a..106a261  master     -> origin/master
Updating 72a914a..106a261
error: Your local changes to the following files would be overwritten by merge:
                filename.ext
 

此时,你可以通过还原你的本地更改来克服这个问题,下面是一种方法:

 $ git checkout filename.ext

然后再次拉取并重新修改本地文件,或者可以设置--no-assume-unchanged,这样你可以在那个点上进行正常的stash和merge操作等。


18
这个命令是在本地还是在.git文件夹中执行其操作?我的意思是,如果我对config.php文件运行此命令,这会传播到使用该存储库的其他用户吗? - Magus
22
@Magus: 不行,这只会对你起作用。 - Rob Wilkerson
4
很快你就会想知道如何确定一个文件是否被认为是未更改的:https://dev59.com/inE95IYBdhLWcg3wUMPW - Ciro Santilli OurBigBook.com
10
使用这种方式忽略的文件更改将在使用 "git stash" 命令时丢失。有没有避免这种情况的方法? - Alexis
6
git update-index --assume-unchanged 的作用不是这个。 - jsageryd
显示剩余9条评论

118
首选的方法是使用 git update-index --skip-worktree <file>,如本答案所述:

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

skip-worktree不仅仅是这样:即使Git知道文件已被修改(或需要通过重置--hard或类似操作进行修改),它也会假装未被修改,使用来自索引的版本。这将持续到索引被丢弃为止。

要撤消此操作,请使用git update-index --no-skip-worktree <file>

自 Git 版本2.25.1 起,这也不再是推荐的方法,引用:

用户通常尝试使用 assume-unchanged 和 skip-worktree 位告诉 Git 忽略已跟踪的文件的更改。这不会按预期工作,因为 Git 在执行某些操作时仍可能检查工作树文件与索引的差异。通常情况下,Git 不提供一种忽略已跟踪文件更改的方法,因此建议使用其他解决方案。

例如,如果您要更改的文件是某种配置文件,则存储库可以包含一个示例配置文件,然后将其复制到被忽略的名称并进行修改。甚至存储库可以包括一个脚本来将示例文件作为模板处理,自动修改和复制它。


1
这是否适用于检查存储库的所有用户?他们会得到某些文件,但除非明确说明,否则不能再添加更改,误操作吗? - mjs
3
@momomo,这个标记存储在索引中,所以不,它只适用于单个用户。请参阅erjiang的答案,以获取适用于所有用户的解决方案。 - 1615903
2
Github文档明确表示不应该使用git update-index --skip-worktree来实现此目的。 - bk2204
1
@Emir,这种方法的效果一如既往地好,只是Git已经更新了文档以指出他们不推荐使用它。其他答案涵盖了其他选项 - 这主要适用于您无法控制远程并需要某种解决方法的情况。 - 1615903
1
谢谢,好的了解。我想这是我的错误。我误解了“不推荐”为“不能正常工作”。感谢您的跟进。事实上,这个答案解决了我的问题。 - Emir
显示剩余3条评论

52

通常的做法是创建名为 devtargets.default.rb 的文件并将其提交,然后要求每个用户将该文件复制到 devtargets.rb(该文件在 .gitignore 列表中)。例如,CakePHP也对其数据库配置文件采用同样的方式处理,因为这些配置在不同机器上会自然而然地发生变化。


6
如果一个文件正在被追踪,你无法在.gitignore中忽略它。.gitignore只对不在索引中的文件有效。 - CB Bailey
1
我一直试图避免这样做,但实际上我没有一个好的理由。现在我们正在这样做,我认为记住我需要创建自己的版本而不带有“.default”名称是很麻烦的。 - Derick Bailey
12
但是公平地说,记得复制文件比记得为每个克隆该存储库的人使用 --assume-unchanged 选项要容易得多。 - Dan
1
@DerickBailey,如果没有devtargets.rb文件,你也可以将rake构建设置默认为devtargets.default.rb - Luke Willis
1
我发现唯一令人满意的练习是简单的,不需要一堆人同步执行一堆git命令... 我仍然不知道为什么git没有这个功能,显然很多人都需要。 - Nathan Chappell
显示剩余2条评论

6
无法使用Git忽略跟踪文件的更改。 Git FAQ解释了这个问题:
Git不提供这样做的方法。原因是,如果Git需要覆盖此文件(例如在checkout期间),它不知道文件的更改是否很重要并且应该保留,还是它们是无关紧要的并且可以安全地销毁。因此,它必须采取安全路线并始终保留它们。
尝试使用git update-index的某些功能(即assume-unchanged和skip-worktree位)是很诱人的,但这些功能对于此目的不起作用,并且不应该以这种方式使用。
如果您的目标是使用配置文件,则最好的方法是添加示例或模板文件,然后让用户将其复制到适当的位置,或者让脚本创建适当的文件。然后,您应该忽略实际配置文件的位置,只检查示例或模板。

2

对于IntelliJ IDEA用户:如果您想忽略某个文件(或多个文件)的更改,可以将其移动到不同的更改集中。

  • 进入本地更改Cmd + 9
  • 选择要忽略的文件
  • F6将它们移动到另一个更改集

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