git - 从 Git 中删除文件,但不要为远程用户删除,只是忽略它
问题描述
我和其他几个人都可以访问包含一个由 IDE 自动生成的文件的存储库。这个文件是相当特定于 PC 的,所以不应该在源代码控制中,但目前是。我想将其删除并将其添加到 中.gitignore
,但我不希望在其他协作者拉取我的更改时将其删除。关于删除文件但保留我的本地副本有很多问题;但他们不涵盖其他用户,所以当他们拉出时,尽管我保留了我的副本,但他们仍然会丢失他们的副本:
也有拉的时候不丢失本地文件的问题和解决方案,所以他们可以保留文件,但这需要那些拉的明确的动作,我不想去告诉大家具体如何拉这个时间。我确实发现了两个重复的问题。那里的答案是它无法完成,但它们都是 5 年前 - 在此期间有什么变化吗?
这很重要,因为该文件是在您首次导入整个项目时自动生成的,并且包含有关本地编译器/库版本的信息。所以删除它需要重新导入。如果它有任何区别,那就是.idea/scala_compiler.xml
and (实际上应该忽略.idea/scala_settings.xml
整个目录)。.idea
基本上我想让 Git 将一个文件设置为不再跟踪,但不要为任何人删除它。
解决方案
你不能。
嗯,让我再试一次:你不能,但他们可以。好吧,你可以,但只为你,他们可以,但只为他们。你,或者他们,必须git rm --cached
在正确的时间运行。当然,这是您不想使用的解决方案。
更有用的是(冒着重复前面问题的风险):对于这些文件,就 Git 提交而言,您唯一能做的就是在未来的 Git 提交中忽略它们。由于不在提交中,它们也不会通过推送和获取操作传输。
请记住,每个提交都包含Git 知道的所有文件的完整快照。 (我们稍后会进一步完善。)如果 Git 知道.idea/*
,Git 会将它们放入新的提交中,当你推送这些提交时——你不能推送文件,只能提交——那些提交,完成那些文件,将四处走动。当你获取新的提交时——同样,你得到的是整个提交,而不是文件——这些提交将与这些文件一起出现。
那么根本问题就变成了这样:
- 你或他们正在进行 Git 知道的提交
.idea/*
。您当前的提交包含文件。 - 你,或者他们,已经获取了一些新的提交。这些新提交不包含这些
.idea/*
文件。 - 如果您(或他们)现在要求您(或他们的)Git 将您从当前提交切换到缺少文件的提交,您(或他们)的 Git 会看到您(或他们)明确告诉您(他们) Git删除文件。所以它会这样做。
这个问题的解决方法是:
您(他们)必须告诉您(他们的)Git现在忘记这些文件,以便这些文件的工作树副本不被跟踪:
git rm -r --cached .idea # note the --cached
现在你(他们)告诉你的 Git:切换到新的 commit。未跟踪的文件根本不在 Git 的视图中,也不在新提交中,因此 Git不会删除这些文件的工作树副本。
请注意,如果您切换回包含这些文件的旧提交,您的 Git 将使用提交的文件覆盖您的工作树文件。(他们的 Git 将在相同条件下对他们的工作树文件执行相同的操作。)因此,在返回包含这些文件的历史提交时要非常小心。有关详细信息,请参阅下面的详细说明。
龙:这是怎么回事
正如我们刚刚提到的,每次提交都有每个文件的完整快照。这些快照以特殊的、只读的、仅限 Git 的格式保存。我喜欢称这种格式为冻干。这种形式的文件会自动进行重复数据删除,因此大多数提交主要重用以前提交的大多数文件这一事实意味着新提交几乎不会占用任何磁盘空间。
Git 重新使用这些冻干文件是安全的,因为任何现有提交的任何部分,包括保存的文件,都不能被更改。您可以进行与现有提交不同的新提交,但不能更改现有提交。甚至 Git 本身也无法做到这一点。
因为你实际上不能使用这些文件来做任何实际的工作,所以 Git 必须提取一个提交。这就是git checkout
(或者,从 Git 2.23 开始git switch
)所做的:它从某个提交中提取冻干文件,并将其提取为您可以实际使用(和更改)的形式。您选择提取,然后使用和/或处理的提交是您当前的提交。
这意味着从当前提交中获取的每个文件实际上都有两个副本:与提交本身一起存储的冻干副本,以及用于执行实际工作的常规格式、再水化的副本。
要进行新的提交,任何使用这种方案的版本控制系统(大多数都这样做,尽管内部细节差异很大)必须采用您当前的工作树版本并将它们转换回适当的已提交版本。在大型存储库中,这可能需要相当长的时间。为了让自己更容易,Git 实际上并没有这样做。
取而代之的是,Git 保留了第三份副本——嗯,不是真正的副本,确切地说,因为它使用冻干、去重的格式——在 Git 所谓的index或staging area或(这些天很少)缓存中。这个缓存的、冻干格式的、预先去重的文件副本已准备好进入您将进行的下一次提交。
让我们用粗体重复一遍,因为它是这里的关键:Git 的索引包含将进入下一次提交的文件,采用冻干格式,准备就绪。git checkout
or操作从git switch
提交中填充 Git 的索引和您的工作树,现在是当前提交。现在所有三个副本都匹配,除了工作树副本实际上是可用的,而不是被冻干。
如果您更改工作树副本,则必须git add
在其上运行。该git add
命令告诉 Git:使您的索引副本与我的工作树副本匹配。 Git 现在将读取工作树副本并将其压缩和重复数据删除为冻干格式,准备进入下一次提交。因此索引中的文件不再匹配当前提交中的文件。换句话说,索引和提交之间的一个关键区别是您可以通过像这样批量替换文件来更改索引内容。
从字面上看,这些索引副本是Git 知道的文件。它们是将在下一次提交中的文件。为确保下一次提交没有文件,您只需将其从 Git 的索引中删除即可。
git rm
命令_
该git rm
命令从 Git 的索引中删除文件。如果没有--cached
,它还会从您的工作树中删除这些文件。您想保留您的工作树副本,因此您需要告诉 Git:保留我的工作树副本,方法是添加--cached
到您的git rm
: 仅从索引中删除(“缓存”)。
现在该文件或文件不在Git 的索引中,它们不会在下一次提交中。因此,一旦删除文件,您就可以进行没有文件的新提交:
git rm -r --cached .idea && git commit
例如。
切换提交
当你使用git checkout
或git switch
从一个提交切换到另一个提交时——例如,通过更改你所在的分支——你是在告诉 Git:删除与当前提交相关的所有内容并切换到另一个提交。 这让 Git 清空它的索引,删除每个相应文件的工作树副本——Git 知道的文件。然后,Git 可以重新填充其索引并使用您想要处理/使用的提交中的文件副本重新填充您的工作树:您的新当前提交。
如果 Git 知道.idea/*
,这就是.idea/*
文件被删除的原因。如果他们不在新的提交中,他们不会从新的提交中回来。
.gitignore
有一个粗心的陷阱
该.gitignore
文件的名称有些错误。中列出的文件.gitignore
不一定是untracked,如果它们被跟踪——如果 Git 知道它们,因为它们在 Git 的索引中——它们根本不会被忽略。
让我们在这里注意,一个未跟踪的文件现在在你的工作树中,但现在不在Git 的索引中。这意味着如果.idea/*
被跟踪——例如,从当前提交中出来——但你只是运行git rm --cached .idea/*
or git rm -r --cached .idea
,那些工作树副本现在未被跟踪。它们是否在当前提交中并不重要:重要的是它们是否在 Git 的索引 中。
告诉Git.gitignore
三件事。前两个通常是重要的两个。最后一个是陷阱。
如果未跟踪文件的名称或模式出现在 中
.gitignore
,则该git status
命令不会抱怨该文件未跟踪。如果未跟踪文件的名称或模式出现在 中
.gitignore
,git add
则不会将该文件添加到Git 的索引中(git add
如果需要,您可以强制覆盖它)。这意味着该文件将在正常的日常git add
s 中保持未跟踪。如果未跟踪文件的名称或模式列在 中
.gitignore
,Git 有时会随意破坏该文件。
当你切换提交时,Git 尽量不破坏未保存的工作
您可能对这个问题很熟悉:您开始处理某个文件——即工作树中的副本——然后意识到:哎呀,我想在不同的分支上完成这项工作。 你运行or ,Git 用它有点神秘的方式说:我不能那样做。 Git 告诉你有未保存的更改会被破坏。git checkout branch
git switch branch
(有时 Git 会让你切换分支。这一切都与 Git 的索引有关。有关血腥细节,请参阅Checkout another branch when there are uncommitted changes on the current branch)
如果此未保存的作品位于跟踪文件中,或者位于未在 中列出的未跟踪文件中,.gitignore
则此安全检查将防止您丢失数据。但是在其中列出文件有时会.gitignore
允许Git 覆盖或删除工作树副本。发生这种情况的确切时间并不明显——有时即使这样,Git 也会告诉你先保存文件——但这是一个问题。
唯一完整的解决方案是痛苦的
不幸的是,这个问题的唯一真正解决方案与问题本身一样痛苦,或者比问题本身更痛苦:您可以使用包含文件的提交的存储库,并使用它来构建一个新的、不兼容的编辑历史存储库,仅包含根本没有文件的提交。
为此,请使用git filter-branch
、 或git filter-repo
(相对较新且尚未随 Git 本身分发)、BFG 或任何此类 Git-commit-history-editing 系统。所有这些工作的方式,必然是它们将旧提交(那些拥有文件的提交)复制到具有不同哈希 ID 的新提交,这些文件永远不会出现在其中。然后,此更改会“随着时间的推移而下降”到所有后续提交中。这就是使新存储库与旧存储库不兼容的原因。
如果您曾经让旧存储库和新存储库相遇,并且有任何相关的历史没有改变,1这两个 Git 将连接旧历史和新历史,并且您实际上将存储库的大小翻倍,同时添加回所有你认为你已经摆脱的提交。
1这将是在不需要的文件存在之前的历史提交。例如,如果您使用 GitHub 的以README.md
和LICENSE
文件开头的技巧,则该提交不需要重写,并且将保持不变并在新旧存储库之间建立共同的提交历史。
除此之外,如果您使用可以追溯到--allow-unrelated-histories
标志之前的旧 Git,或者提供--allow-unrelated-histories
到git merge
,也可以将旧历史融合回新历史。
推荐阅读
- jquery - 从 jquery 获取数据在控制器中创建的项目
- matomo - Matomo 不再跟踪动作
- c# - TcpListener 和 WebSocket 失败:一个或多个保留位打开:reserved1 = 1,reserved2 = 0,reserved3 = 0
- python-3.x - 如何在 Python 中覆盖多行打印?
- java - 使用 Java 读取 CSV 文件包含 Spark 中的结构类型
- java - 为什么杰克逊将 ZonedDateTime 转换为十进制?
- vue.js - 是否有可能在 Nuxt.js 中使用 gRPC?
- asp.net - Server 2016 AD 和 IIS Express 无法设置用户密码但可以创建用户
- azure - Azure 事件中心 - 虚拟网络集成 - 限制阅读
- machine-learning - 如何知道 pytorch 模型的输入形状?