如何永久git rm --cached文件?

时间:2019-02-28 20:04:36

标签: git github

当我对文件执行git rm --cached文件名以将其更改排除在git暂存区和存储库区域之外时,我没有问题,但是下次我执行一次提交时,我必须执行相同的git rm- -cached filename再次避免更改。

如何使用git rm --cached文件永久排除更改?

谢谢

1 个答案:

答案 0 :(得分:3)

简短的答案是您不能这样做。 git rm --cached所做的只是从索引中删除一个条目(或多个条目)。总是可以将其他条目放回去。

这是TL; DR,您可以根据需要在此处停止。但是,如果您想应该处理这些文件,请继续阅读...

但是,这不是表达从索引中删除条目的效果的好方法:

  

...当我对文件执行git rm --cached文件名以获取其更改时,将其排除在git暂存和存储库区域

之外

(强调我的),因为索引不存储更改。这里有一些重要的术语问题,如果您不太精确的话,以后可能会造成一定程度的痛苦。因此,让我们在这里更加清楚,因为Git已经非常令人困惑(我已经使用它十多年了,它仍然有一些黑暗的角落)。

关于索引

所以,我们来谈谈索引。这个东西(这个索引)在Git中至关重要,因为这是您提交 next 的方式。因此,Git的某些部分将其称为临时区域。 Git的一些部分(例如git rm --cached本身)称为 cache 。这只是同一事物的三个术语:对于Git随身携带的,松散地连接到您的工作树的数据结构,Git在进行 new 提交时将使用的数据结构。

索引本身是一个复杂的小野兽,在合并过程中扮演着扩展的角色,但是它主要要做的是保存文件的副本。要了解这部分是如何工作的,有助于查看 commits work-tree (或工作树,工作目录或任意数量的相似术语)。提交,或更确切地说,是指所有文件的完整快照。这里没有更改,只有文件。如果提交中有文件main.py,则提交具有 of main.py的完整副本,其中包含您(或任何人)运行git commit时的内容。

提交中的文件被有意地冻结和压缩(有时是非常压缩的),并且将Git修改为只有Git才能真正使用的形式。这对于永久归档提交非常有用,但是对于完成任何工作都没有好处。为了完成工作,Git必须将文件解冻,解压缩和解Git化为您通常使用的格式。这就是您的工作树的来源:这是您可以进行工作的地方。

那么,您可能会认为Git会在这里停止:您将拥有永久冻结的提交以及您的工作树;然后您在工作树中更改文件,并告诉Git提交,它将再次冻结工作树文件。但这实际上不是发生的事情。

相反,当您检出提交时,例如,使用git checkout branch – Git 复制从<< / em>提交到索引中。在这里,文件仍然是Git格式的,并且准备好冻结 next 提交中。但是现在它们实际上并没有冻结。 现在,您可以覆盖它们。他们仍然只是Git!

因此,在将文件复制到后,Git现在开始对文件进行解压缩和De-Git化,然后将普通文件放入您的工作树中。这就是说,当您执行进行下一次提交时,Git可以并且确实可以忽略您的工作树。只需获取索引现在中的所有内容,即所有文件的完整副本,并将其冻结到新的提交中即可。

这就是为什么即使您之前git add对某些文件进行了编辑,也必须继续运行git add的原因。 git add所做的是将工作树文件复制到 index 副本中。如果以前在索引中有一个副本,那么现在它会被新的(以及新的经过Git验证的)版本覆盖。如果没有,现在在索引中。无论哪种方式,现在都可以进入 next 提交。

索引中存在文件 正是将文件定义为已跟踪的原因。如果在索引中,则会对其进行跟踪。如果不是,则不是。

因此git rm --cached somefile从索引中删除了somefile的Git认证副本。如果没有--cached,它也会从工作树中删除普通格式的somefile。使用--cached,它将工作树文件留在后面:

$ git checkout master
... bunch of files exist ...

$ git rm --cached main.py     # main.py is now untracked

$ git add main.py             # main.py is now tracked

之间没有发生 commit ,因此我们将文件从已跟踪更改为未跟踪,然后从未跟踪更改为已跟踪。现在索引中的副本与之前索引中的副本匹配—较早的副本是从一次提交中提取出来的,但是由于工作树版本是De-Git化的版本,因此我们将其重新Git化为索引,现在与删除索引之前相同。

如果我们将文件保留在索引之外(再次为git rm --cached),然后为git commit。我们进行的 new 提交,现在没有文件。 (新的提交也将成为分支的尖端提交。)因此,如果稍后我们git checkout对该 new 提交,则该文件不在中,因此索引中不是 。如果工作树中有该名称的未跟踪文件,则该未跟踪文件不会受到干扰。

但是,一旦我们检查出确实存在的任何较早的提交,那么,它就在提交中,因此Git将其复制到索引中,然后再复制到工作树中。现在,它再次被跟踪,因为它现在在索引中。如果我们已经完成了对旧提交的查看,而我们git checkout {em>没有没有文件Git的新提交:

  1. 看到文件在提交和索引以及工作树中
  2. 发现新提交中的文件不是
  3. 切换到新提交时,从索引和工作树中删除文件

关于.gitignore个文件

.gitignore文件中列出文件名或与文件名匹配的模式并不能告诉Git确切地忽略该文件。它的作用是影响未跟踪的文件。

正如我们已经提到的,未跟踪的文件是在工作树中但不在索引中的文件。它是否在给定的提交中无关紧要,重要的是它是否现在在索引 中。如果您将文件添加到索引,请使用git add将其从工作树复制(并压缩并Git-ify)到工作树中,然后索引就可以了。好吧,如果您从索引中git rm --cached开始访问文件,则该文件不存在,因此无法跟踪。随意来回交换!真正重要的事情还没有发生:您只是将文件输入索引或从索引中删除文件。

未被跟踪有两个主要影响:

  • 如果并且在您运行git commit时,它不会进入 next 提交。这是因为Git使用索引中的任何内容构建新的提交。
  • 而且,它使Git向您发牢骚:嘿,文件somefile未被跟踪,您是否不想在提交中使用它?

.gitignore中列出文件名会使Git陷入无法追踪的境地, 使git add --allgit add . 不会将该文件复制到索引(如果尚不存在)。因此,我们可以说该文件应该称为.gitignore而不是.git-stop-whining-about-these-files-and-do-not-automatically-add-them-with-mass-add-operations

这个名字不好用,所以Git使用.gitignore

但是,不幸的是,在.gitignore中列出文件会产生副作用。对于未跟踪的文件,Git通常非常小心。假设文件未跟踪,并且某些操作会使文件变得混乱。例如,假设somefile目前未跟踪,并且也不在当前提交中。但这是分支dev的最先提交,您告诉Git:git checkout devgit merge dev。 (合并的情况比较复杂,但存在相同的问题。)

由于文件,位于dev的提交中,因此Git将需要从该提交中将somefile复制到索引以及工作树。这将覆盖 somefile。 Git会告诉您:不,我不会破坏您的文件somefile。它没有被追踪并且在路上。请提交,或将其移开。

但是,如果您在somefile中列出.gitignore,Git将会随意掩盖 ,至少在某些情况下(包括git checkout dev一)。因此,该.gitignore文件 应该命名为.git-stop-whining-about-these-files-and-do-not-automatically-add-them-with-mass-add-operations-but-do-feel-free-to-clobber-them-in-some-cases

要点:没有完美的答案

无法更改现有的提交,因此,如果某些文件进入了某些本不应该存在的提交中,则您将陷入困境。您可以进行某些所谓的“历史重写”(例如,使用git filter-branchThe BFG)。这就带来了新的和改进的提交,这些提交具有不同的哈希ID(没有文件),然后您必须说服所有人使用它来代替具有文件的旧的错误提交。

或者,您可以git rm --cached,然后选择是否在.gitignore中列出该文件。如果可以安全使用,请在此处列出可能是可行的方法。这样,文件就不会在您从此处开始的 new 提交中的索引中。当然,如果您回到旧提交并使用它们(使用填充到索引中的内容)进行新提交,而忘记再次git rm --cached,那将无济于事,因为一旦文件 在您的索引中,您实际上必须删除,方法是切换回不包含该提交的提交,或者对其运行git rm --cached。但是git add .不会意外添加。返回到确实包含文件的较早提交时,只需注意不要将文件 clobber 从工作树中删除。

或者,当然,您可以git rm --cached保留文件并忍受任何抱怨。