从 Git 中删除文件,但不删除远程用户,只是忽略它

Y_L*_*ess 9 git ignore intellij-idea

我和其他几个人都可以访问包含 IDE 自动生成的一个文件的存储库。该文件是特定于 PC 的,因此不应位于源代码管理中,但目前是。我想删除它并将其添加到.gitignore,但我不希望其他协作者在拉取我的更改时将其删除。关于删除文件但保留本地副本存在很多问题;但他们不覆盖其他用户,所以当他们拉动时,尽管我保留了我的副本,他们仍然会丢失副本:

从 Git 存储库中删除文件,而不将其从本地文件系统中删除

如何 git rm 文件而不将其从磁盘中删除?

还有一些问题和解决方案,可以在拉取时不丢失本地文件,这样他们就可以保留文件,但这需要拉取时的明确操作,我不想去告诉每个人到底如何拉取这个文件时间。我确实发现了两个重复的问题。得到的答案是不可能,但那都是 5 年前的事了——这期间有什么变化吗?

Git 删除跟踪的文件,但保留本地和远程

Git 忽略文件,而不删除它

这很重要,因为该文件是在您首次导入整个项目时自动生成的,并且包含有关本地编译器/库版本的信息。所以删除它需要重新导入。如果有什么区别的话,那就是.idea/scala_compiler.xmland .idea/scala_settings.xml(实际上整个.idea目录应该被忽略)。基本上我想让 Git 将一个文件设置为不再跟踪,但不为任何人删除它。

tor*_*rek 9

你不能。

\n

嗯,让我再试一次:不能,但他们可以。好吧,你可以,但仅限于你,他们也可以,但仅限于他们。你,或者他们,必须git rm --cached 在正确的时间跑步。当然,这是您不想使用的解决方案。

\n

更有用地说(冒着重复前面问题的风险):就 Git 提交而言,您对这些文件唯一能做的就是从未来的 Git 提交中忽略它们。由于不在提交中,它们也不会通过推送和获取操作进行传输。

\n

请记住,每次提交都包含Git 知道的所有文件的完整快照。 (我们稍后会进一步完善这一点。)如果 Git 知道.idea/*,Git 会将它们放入新的提交中,当您推送这些提交\xe2\x80\x94 时,您无法推送文件,只能提交\ xe2\x80\x94这些提交以及这些文件将继续存在。当您再次获取新的提交\xe2\x80\x94时,您将获得整个提交,而不是文件\xe2\x80\x94,这些提交将随这些文件一起提供。

\n

那么根本问题就变成了这样:

\n
    \n
  • 您或他们正在进行 Git 知道的提交.idea/*。您当前的提交有文件。
  • \n
  • 您或他们已经获取了一些新的提交。这些新提交包含这些.idea/*文件。
  • \n
  • 如果您(或他们)现在要求您(或他们的)Git 将您当前提交切换缺少文件的提交,您(或他们)的 Git 会发现您(或他们)正在明确告诉您(他们) Git删除文件。所以它会这样做。
  • \n
\n

这个问题的解决办法是:

\n
    \n
  • 您(他们)必须告诉您(他们的)Git现在忘记这些文件,以便这些文件的工作树副本不被跟踪:

    \n
     git rm -r --cached .idea      # note the --cached\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
  • 现在你(他们)告诉你的 Git:切换到新的提交。未跟踪的文件根本不在 Git 的视图中,也不在新的提交中,因此 Git不会删除这些文件的工作树副本。

    \n
  • \n
\n

请注意,如果您切换包含这些文件提交,您的 Git 将用提交的文件覆盖您的工作树文件。(在相同条件下,他们的 Git 会对其工作树文件执行相同操作。)因此,在返回包含这些文件的历史提交时要非常小心。有关更多详细信息,请参阅下面的详细说明。

\n

Long:这是怎么回事

\n

正如我们刚才提到的,每次提交都有每个文件的完整快照。这些快照以特殊的、只读的、仅限 Git 的格式保存。我喜欢称这种格式为冻干。这种形式的文件会自动进行重复数据删除,因此大多数提交大多重复使用先前提交中的大多数文件,这意味着新提交几乎不占用任何磁盘空间。

\n

Git 重复使用这些冻干文件是安全的,因为任何现有提交的任何部分(包括保存的文件)都无法更改。您可以进行与现有提交不同的新提交,但无法更改现有提交。甚至 Git 本身也无法做到这一点。

\n

因为您实际上无法使用这些文件来执行任何实际工作,所以 Git 必须提取提交。这就是git checkout(或者,从 Git 2.23 开始git switch)所做的:它从某个提交中提取冻干文件,并将其转换为您可以实际使用(和更改)的形式。您选择提取并使用和/或处理的提交是您当前的提交

\n

这意味着从当前提交中获取的每个文件实际上有两个副本:与提交本身一起存储的冻干副本,以及用于执行实际工作的常规格式、重新水合的副本。

\n

要进行新的提交,任何使用这种方案的版本控制系统\xe2\x80\x94(尽管内部细节差异很大)\xe2\x80\x94 都必须采用当前的工作树版本并将它们转回适当的提交版本。在大型存储库中,这可能需要相当长的时间。为了让自己更容易,Git 实际上根本不这样做。

\n

相反,Git 保留了第三个副本\xe2\x80\x94,确切地说,并不是真正的副本,因为它使用冷冻干燥、去重复的格式\xe2\x80\x94,Git 称之为索引暂存区,或者(现在很少)缓存。这个缓存的、冻干格式的、预先去重复的文件副本已准备好进入您将进行的下一次提交。

\n

让我们用粗体重复一遍,因为它是这里的关键: Git 的索引包含将进入下一次提交的文件,以冻干格式,准备就绪。orgit checkout操作git switch从提交中填充 Git 的索引和工作树,该提交现在是当前的。现在,所有三个副本都匹配,只是工作树副本实际上可用,而不是被冻干。

\n

如果更改工作树副本,则必须git add在其上运行。该git add命令告诉 Git:使您的索引副本与我的工作树副本匹配。 Git 现在将读取工作树副本并将其压缩和去重复为冻干格式,准备进入下一次提交。因此索引中的文件不再与当前提交中的文件匹配。换句话说,索引提交之间的一个关键区别是您可以通过像这样批量替换文件来更改索引内容。

\n

从字面上看,这些索引副本是Git 知道的文件它们是将在下一次提交中的文件。确保下一次提交不会某些文件,您只需将其从 Git 索引中删除即可。

\n

git rm

\n

git rm命令从 Git 索引中删除文件。如果没有--cached,它还会从您的工作树中删除这些文件。你想保留你的工作树副本,所以你需要告诉 Git:通过添加到你的工作树副本来保留我的工作树副本--cachedgit rm:remove 仅从索引(“缓存”)来

\n

既然该文件或多个文件不在Git 的索引中,它们就不会出现在下一次提交中。因此,一旦删除文件,您就可以进行新的提交,但不会包含这些文件的新提交:

\n
git rm -r --cached .idea && git commit\n
Run Code Online (Sandbox Code Playgroud)\n

例如。

\n

切换提交

\n

当您使用git checkoutgit switch从一个提交切换到另一个提交时,通过更改您所在的分支(例如,\xe2\x80\x94),您正在告诉 Git:删除与当前提交相关的所有内容并切换到其他提交。 这会让 Git 清空其索引,删除每个对应文件\xe2\x80\x94(Git 知道的文件)的工作树副本。然后,Git 可以重新填充其索引,并使用您想要处理/处理的提交中的文件副本重新填充您的工作树:新的当前提交。

\n

如果 Git 知道.idea/*,这就是导致.idea/*文件被删除的原因。如果它们不在新提交,则它们不会从新提交中返回。

\n

.gitignore对于粗心的人来说有一个陷阱

\n

.gitignore文件的命名有些错误。中列出的文件.gitignore不一定是未跟踪的,如果它们被跟踪\xe2\x80\x94(如果 Git 知道它们,因为它们位于 Git 的索引\xe2\x80\x94 中),那么它们根本不会被忽略。

\n

请注意,未跟踪的文件现在位于您的工作树中,但现在不在Git 的索引中。这意味着,如果在当前提交中跟踪\xe2\x80\x94,例如\xe2\x80\x94,但您刚刚运行或,则这些工作树副本现在未跟踪。它们是否在当前提交中并不重要:重要的是它们现在是否在 Git索引中。.idea/*git rm --cached .idea/*git rm -r --cached .idea

\n

所做.gitignore的就是告诉 Git 三件事。前两个通常是重要的两个。最后一个是陷阱。

\n
    \n
  1. 如果未跟踪的文件的名称或模式出现在 中.gitignore,则该git status命令不会抱怨该文件未跟踪。

    \n
  2. \n
  3. 如果未跟踪的文件的名称或模式出现在 中.gitignoregit add则不会将该文件添加Git 的索引中(git add如果需要,您可以强制覆盖它)。这意味着该文件在正常的日常生活中将保持不被跟踪git add

    \n
  4. \n
  5. 如果未跟踪的文件的名称或模式在 中列出.gitignore,Git 有时会随意破坏该文件。

    \n
  6. \n
\n

当您切换提交时,Git 会尝试不破坏未保存的工作

\n

您可能熟悉这个问题:您开始处理某些文件\xe2\x80\x94工作树中的副本,即\xe2\x80\x94,然后意识到:哎呀,我想在不同的分支上完成这项工作。 你运行or ,Git 以一种有点神秘的方式说:我不能这样做。git checkout branchgit switch branch Git 告诉您有未保存的更改,这些更改将会被破坏。

\n

(有时 Git 无论如何都会让你切换分支。这又与 Git 的索引有关。有关详细信息,请参阅当当前分支上有未提交的更改时签出另一个分支

\n

如果此未保存的作品位于跟踪文件中,或者位于未中列出的未跟踪文件中.gitignore,则此安全检查将防止您丢失数据。但列出文件有时.gitignore会允许 Git 覆盖或删除工作树副本。当这种情况发生时并不明显\xe2\x80\x94有时即使有了这个,Git 也会告诉你先保存文件\xe2\x80\x94但这是一个问题。

\n

唯一完整的解决办法是痛苦的

\n

不幸的是,解决这个问题的唯一真正的解决方案与问题本身一样痛苦,或者比问题本身更痛苦:您可以采用包含文件的提交的存储库,并使用它来构建一个新的、不兼容的编辑历史存储库仅包含根本没有这些文件的提交。

\n

为此,请使用git filter-branch、 或git filter-repo(相对较新,尚未随 Git 本身一起分发)、BFG 或任何此类 Git 提交历史记录编辑系统。所有这些工作的方式必然是,它们将旧的提交\xe2\x80\x94(包含文件\xe2\x80\x94)复制到新的提交,并具有不同的哈希ID,这些文件永远不会出现在新的提交中。然后,此更改会“随着时间的推移”影响到所有后续提交。这就是导致新存储库与旧存储库不兼容的原因。

\n

如果你曾经让旧的存储库和新的存储库相遇,并且有任何相关的历史记录没有改变1这两个 Git 将连接新旧的历史记录,你的存储库的大小基本上会增加一倍。存储库,同时添加回您认为的所有提交已经删除的

\n
\n

1这将是早于不需要的文件存在的历史提交。例如,如果您使用 GitHub 的以 and 开头的README.md技巧LICENSE文件开头的技巧,则该提交不需要重写,并且将保持不变并在新旧存储库之间建立公共提交历史记录。

\n

除此之外,如果您使用可以追溯到该--allow-unrelated-histories标志之前的旧 Git,或者提供--allow-unrelated-historiesgit merge,也可以将旧历史记录重新融合到新历史记录中。

\n