当我对文件执行git rm --cached文件名以将其更改排除在git暂存区和存储库区域之外时,我没有问题,但是下次我执行一次提交时,我必须执行相同的git rm- -cached filename再次避免更改。
如何使用git rm --cached文件永久排除更改?
谢谢
答案 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的新提交:
.gitignore
个文件在.gitignore
文件中列出文件名或与文件名匹配的模式并不能告诉Git确切地忽略该文件。它的作用是影响未跟踪的文件。
正如我们已经提到的,未跟踪的文件是在工作树中但不在索引中的文件。它是否在给定的提交中无关紧要,重要的是它是否现在在索引 中。如果您将文件添加到索引,请使用git add
将其从工作树复制(并压缩并Git-ify)到工作树中,然后索引就可以了。好吧,如果您从索引中git rm --cached
开始访问文件,则该文件不存在,因此无法跟踪。随意来回交换!真正重要的事情还没有发生:您只是将文件输入索引或从索引中删除文件。
未被跟踪有两个主要影响:
git commit
时,它不会进入 next 提交。这是因为Git使用索引中的任何内容构建新的提交。somefile
未被跟踪,您是否不想在提交中使用它? 在.gitignore
中列出文件名会使Git陷入无法追踪的境地, 使git add --all
或git 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 dev
或git 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-branch
或The BFG)。这就带来了新的和改进的提交,这些提交具有不同的哈希ID(没有文件),然后您必须说服所有人使用它来代替具有文件的旧的错误提交。
或者,您可以git rm --cached
,然后选择是否在.gitignore
中列出该文件。如果可以安全使用,请在此处列出可能是可行的方法。这样,文件就不会在您从此处开始的 new 提交中的索引中。当然,如果您回到旧提交并使用它们(使用填充到索引中的内容)进行新提交,而忘记再次git rm --cached
,那将无济于事,因为一旦文件 在您的索引中,您实际上必须删除,方法是切换回不包含该提交的提交,或者对其运行git rm --cached
。但是git add .
不会意外添加。返回到确实包含文件的较早提交时,只需注意不要将文件 clobber 从工作树中删除。
或者,当然,您可以git rm --cached
保留文件并忍受任何抱怨。