如何让Git忘记一个曾经被跟踪但现在在.gitignore中的文件?

8077

我将一个之前被 Git 追踪的文件放到了.gitignore清单中。然而,即使在编辑过后,该文件仍然出现在 git status 中。我该如何强制 Git 完全忘记这个文件?


29
git clean -X听起来很相似,但这不适用于仍被Git跟踪的文件。我写这篇文章是为了帮助任何正在寻找解决方案的人不要走错路。 - imz -- Ivan Zakharyaschev
52
唯一真正的答案在下面,查看 git update-index --assume-unchanged。这个解决方案 1) 保留文件在服务器上(索引),2) 让您在本地自由地修改它。 - Qwerty
17
你需要使用 --skip-worktree,请参考:https://dev59.com/nWYr5IYBdhLWcg3wfaSc#13631525。 - Doppelganger
154
一个重要的问题是:这个文件应该留在仓库中还是不应该?例如,如果有人新克隆了仓库,他们应该获得这个文件吗?如果是 _YES_,那么 git update-index --assume-unchanged <file> 是正确的,这个文件将会留在仓库中并且更改不会被添加到 git add 中。如果是 NO (例如缓存文件、生成的文件等),那么 git rm --cached <file> 将会从仓库中移除它。 - Martin
17
每个人都应该停止建议使用 --assume-unchanged,这是为了提高性能,以防止 Git 检查大型跟踪文件的状态,而更喜欢使用 --skip-worktree,这是用于修改过的已跟踪文件,用户不再想提交的文件。请参见 https://dev59.com/nWYr5IYBdhLWcg3wfaSc#13631525 - Philippe
显示剩余4条评论
33个回答

8668

.gitignore可以防止未跟踪的文件被添加到Git跟踪的文件集中(如果没有使用add -f)。但是,Git将继续跟踪任何已经被跟踪的文件。

要停止跟踪一个文件,我们必须从索引中删除它:

git rm --cached <file>

要递归地删除一个文件夹及其内部的所有文件:

git rm -r --cached <folder>

从主干上删除文件将在下一次提交时发生。

警告:虽然这不会从您的本地计算机中删除物理文件,但它会在其他开发人员进行下一次git pull时从他们的计算机中删除这些文件。


78
对我来说行之有效的过程是:
  1. 先提交未处理的更改
  2. 运行命令 git rm --cached <文件名> 并再次提交
  3. 将该文件添加到 .gitignore 文件中,使用 git status 命令检查,并再次提交。
- mataal
152
非常重要的补充。如果被忽略的文件被修改了(但是尽管如此不应该提交),在修改后执行 git add . 命令会将其添加到索引中,下一次提交会把它提交到仓库中。为了避免这种情况,在执行完所有mataal说的操作后,请立即执行以下命令: git update-index --assume-unchanged <路径和文件名> - Dao
40
@AkiraYamamoto 的方法对我也很有效。在我的情况下,由于我的代码库有成千上万个文件,我抑制了输出结果:git rm -r -q --cached . - Aaron Blenkush
118
在执行git pull时,这将会删除该文件。 - Petr Peller
34
git rm --cached <file> 可以将文件从代码库中删除,而 git update-index --assume-unchanged <file> 可以让该文件不显示在未暂存的更改中,并且不会拉取新的更改。但我希望 Git 完全忽略该文件的内容,请帮我实现这个要求。 - Igor Semin
显示剩余43条评论

3422
以下一系列命令将从 Git 索引中删除所有项(而不是从工作目录或本地仓库中删除),然后会在尊重 Git 忽略的情况下更新 Git 索引。 PS. 索引 = 缓存 首先:
git rm -r --cached .
git add .

那么:

git commit -am "Remove ignored files"

或者简短地说:

git rm -r --cached . && git add . && git commit -am "Remove ignored files"

268
为了突出这个答案与被接受的答案之间的区别:使用这些命令,您不需要实际知道受影响的文件。(想象一个带有许多随机文件应该从索引中清除的临时目录)。 - Ludwig
82
和已接受的答案一样。在执行 git pull 命令时,文件将被删除。 - Petr Peller
98
希望将这个功能作为标准的 git 命令。例如 git rmignored - Berik
15
@gudthing -r代表“递归”。 - Mark
19
使用这种方法,你可能会添加其他当前未列在 .gitignore 中的无用文件。如果你的 git status 太杂乱,可能很难找出这些文件。最好使用一个仅删除新忽略文件的命令。这就是为什么我更喜欢 thSoft 的回答 - KurzedMetal
显示剩余19条评论

1827

git update-index 可以胜任这个工作:

git update-index --assume-unchanged <file>

注意: 这个解决方案实际上与 .gitignore 无关,因为 gitignore 只作用于未跟踪的文件。


更新,更好的选项

自从这个答案发布以来,一个新的选项已经被创建,应该优先使用。您应该使用 --skip-worktree 来处理修改过的已跟踪文件,用户不想再提交它们,并使用 --assume-unchanged 提高性能,以防止 git 检查大型已跟踪文件的状态。有关更多详细信息,请参见https://dev59.com/nWYr5IYBdhLWcg3wfaSc#13631525 ...

git update-index --skip-worktree <file>
取消。
git update-index --no-skip-worktree <file>

253
这就是真正的答案。实际上非常棒,非常简单,不会干扰git status,而且非常直观。谢谢。 - Pablo Olmos de Aguilera C.
33
git update-index --assume-unchanged <path> …会导致 Git 忽略指定路径的更改,无论 .gitignore 中是否包含该路径。如果你从远程拉取数据,并且该远程仓库对该路径进行了修改,Git 将在合并时失败并抛出冲突,需要手动解决冲突。git rm --cached <path> …会导致 Git 停止跟踪该路径。如果您没有将该路径添加到.gitignore中,则在以后的 git status 中将看到该路径。第一个选项可减少 Git 提交历史中的噪音,并允许将来分发“被忽略”的文件的更改。 - ManicDee
40
我对为什么这不是被接受的答案感到相当困惑。这里被接受的答案明显没有回答实际提出的问题。这个答案忽略了在存储库中的文件的更改,同时没有将其从存储库中移除。 - Dave Cooper
26
如果这个答案解释了给定命令的具体作用,比如它与其他建议的解决方案有何不同,那么它将会更加有用。请注意使翻译通俗易懂,但不要改变原文的意思。 - LarsH
12
这个命令只会在你的机器上起作用,对吗? 如果我想停止在所有机器上跟踪这个文件怎么办? 包括将来克隆存储库的机器? - George
显示剩余27条评论

380
git ls-files -c --ignored --exclude-standard -z | xargs -0 git rm --cached
git commit -am "Remove ignored files"

这个命令会获取被忽略文件的列表,从代码仓库中删除它们,并提交更改。


7
如果您也需要将它们从工作目录中移除,只需运行git ls-files --ignored --exclude-standard | xargs git rm。我认为这是最好的答案!因为它非常清晰、符合Unix方式,并且以直接的方式完成所需操作,而不会产生其他更复杂命令的副作用。 - imz -- Ivan Zakharyaschev
6
很好的答案;但是,如果您的路径中间有空格,例如:“My dir/my_ignored_file.txt”,则该命令将失败。 - David H.
8
git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm --cached这段命令的意思是列出被Git忽略的文件,并将其转化为双引号包含的形式,然后使用xargs命令将它们从Git缓存中删除。 - David H.
5
ls-files没有匹配到任何内容,git rm将会报错。使用xargs -r git rm ...告诉xargs如果没有匹配到文件,则不要运行git rm - Wolfgang
11
最好使用\0作为分隔符: git ls-files --ignored --exclude-standard -z|xargs -0 git rm --cached - Nils-o-mat
显示剩余6条评论

145
复制/粘贴(一行)答案是:
git rm --cached -r .; git add .; git status; git commit -m "Ignore unwanted files"

这个命令不会改变.gitignore文件的内容。它会忽略已经提交到Git仓库的文件,但现在我们已经将它们添加到.gitignore中。
命令git status;是用来查看更改的,可以省略。
最终,它将立即提交更改,并附带消息"忽略不需要的文件"。
如果您不想提交更改,请删除命令的最后一部分(git commit -m "忽略不需要的文件")。

2
它可以被理解为命令行将更改文件*.gitignore的内容。你能否让它变得清晰,即不是这种情况(没有*“编辑:”,“更新:”或类似的 - 答案应该看起来像今天写的)。也就是说,不是直接澄清,而是通过改变书写方式来实现。 - Peter Mortensen

119

先将它移出去,提交更改,然后再将其移回来。

这种方法在过去对我有效,但可能有更好的“Git”方式来实现这一点。


3
如果你想忽略一堆之前未被忽略的文件,这个方法非常有效。但就像你所说,可能有更好的方法来解决这个问题。 - Oskar Persson
这正是我所做的。只需将文件移动到git之外的文件夹,然后执行“git add .”,“git commit”(这将删除文件),然后添加gitignore,引用文件/文件夹,再次提交以将gitignore文件添加到git中,然后复制/移动回文件夹,它们应该被忽略。注意:看起来文件已从GIT中删除,因此可能会从其他检出/拉取中删除它们,如上述解决方案所述,但由于您最初正在复制它们,因此在我看来这不是很重要的问题。只需让团队的其他成员知道即可... - Del
7
似乎这是唯一的方法,我能看到。这是git中一个巨大的漏洞(而非“特性”),一旦你将文件/文件夹添加到.gitignore中,它并不仅仅在那个时间点之后忽略该文件-永远地-无论在哪里。 - JosephK
这对我来说是最好的选择,即使我尝试了其他“更潮”的方式。有时候简单就是最好的。 - Patrick Chu
4
请注意,与其他答案一样,这会在git pull时删除其他人的文件。 - Álvaro González
显示剩余3条评论

109

我总是使用这个命令来删除那些未被跟踪的文件。 一行,Unix风格,干净的输出:

我经常使用这个命令来清除那些未被跟踪的文件。一行代码,Unix式的简洁输出:
git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm -r --cached

它列出了您所忽略的所有文件,将每个输出行替换为带引号的行以处理路径中包含空格的情况,并将所有内容传递给git rm -r --cached以从索引中删除路径/文件/目录。


3
太好了!解决方案完美地起作用了,感觉比删除所有文件再重新添加更正确。 - Jon Catmull
8
我也认为这是“最清晰的”。可能很明显,但是仅运行第一部分命令git ls-files --ignored --exclude-standard,让你首先了解/验证新.gitignore将要排除/删除哪些文件,然后再执行最终的git rm - JonBrave
请注意,某些文件名中含有“恶意”字符,例如\n会导致程序执行失败。我已发布了解决方案以应对这种情况。 - JonBrave
3
另外需要注意的是:在拉取时,这将会导致其他人的工作目录中的文件被删除,对吗? - LarsH
在带有空格的repo上使用filter-branch命令时,我尝试了一下但没有成功:sed: 1: "s/.*/": unterminated substitute in regular expression(似乎在filter-branch之外是可以工作的)。我改用了@JonBrave答案中提到的git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached - goofology
3
Git 2.32破坏了ls-files的行为 - 现在您需要添加“-c”。 - wortwart

95

什么情况下使用这个方法:

  1. 你想要取消跟踪很多文件,或者
  2. 你已经更新了你的.gitignore文件

来源:在基于.gitignore的Git存储库中取消跟踪已添加的文件

假设你已经把一些文件添加/提交到你的Git存储库,并将它们添加到你的.gitignore文件中;这些文件仍然存在于你的存储库索引中。本文将介绍如何摆脱它们。

第一步:提交所有更改

在继续之前,请确保所有更改都已提交,包括你的.gitignore文件。

第二步:从存储库中删除所有内容

为了清除你的存储库,请使用:

git rm -r --cached .
  • rm 是删除命令
  • -r 将允许递归删除
  • --cached 仅从索引中删除文件。您的文件仍将存在。

rm 命令可能是不可挽回的。如果您希望事先尝试它的功能,请添加 -n--dry-run 标志以测试一下。

步骤 3:重新添加所有内容

git add .

第四步:提交

git commit -m ".gitignore fix"

您的代码库很干净 :)

将更改推送到远程仓库,以便在那里看到更改的生效。


3
它不会从远程仓库中删除文件? 如果我想在本地仓库和远程仓库都保留这些文件,但让Git“忘记”它们,怎么办? - Avishay28
1
据我所知,这不会从历史记录中删除文件,因为我们没有使用任何更改历史记录的命令(如果我错了,请纠正我)。这只是通过从git中删除在gitignore中忽略的文件来添加一个新的提交。这些文件将存在于历史提交中。 - Dheeraj Bhaskar
这里的最佳答案虽然有风险,但如果有一个可用于验证一切是否按预期进行的命令,它将变得完美无缺。 - Hashim Aziz

74
如果由于其他人可能需要它而无法使用git rm删除跟踪文件(请注意,即使是您执行git rm --cached,当其他人获取此更改时,他们的文件系统中的文件也将被删除)。这通常是因为配置文件覆盖,身份验证凭据等原因所导致的。请查看https://gist.github.com/1423106,了解人们解决此问题的方法。
总结一下:
  • 让您的应用程序查找一个被忽略的文件config-overide.ini,并使用该文件来覆盖提交的文件config.ini(或者替代地,查找~/.config/myapp.ini或$MYCONFIGFILE)。
  • 提交文件config-sample.ini并忽略文件config.ini,如果必要,使用脚本或类似工具复制该文件。
  • 尝试使用gitattributes的clean/smudge魔术功能自动应用和删除更改,例如将配置文件标记为从另一个分支检出并清理该配置文件以从HEAD检出。这是棘手的问题,我不建议新手用户采用这种方法。
  • 将配置文件保留在专门的部署分支上,永远不要将其合并到主分支。当您想要部署/编译/测试时,请合并该分支并获取该文件。这本质上是使用人类合并策略和额外的git模块的smudge/clean方法。
  • 反向建议:不要使用assume-unchanged,否则只会导致麻烦(因为让git对自己撒谎会导致糟糕的事情发生,例如您的更改将永远丢失)。

7
如果文件在删除时处于脏状态,Git 不会删除它。如果文件不是脏的,则检索该文件就像执行 git checkout <oldref> -- <filename> 一样容易 - 但是这样会被检出并被忽略。 - amenthes
关于你最后的注释(关于 --assume-unchanged):要么这是灵验的,应该被驳回,要么你可以解释为什么(我相信),它变得有用。 - Romain Valeri
1
@RomainValeri:“如果Git需要修改索引中的文件(例如在合并提交时),它将会优雅地失败;因此,如果假定未跟踪的文件在上游发生了更改,则需要手动处理该情况。”—http://git-scm.com/docs/git-update-index。您可以执行以下操作:(1)将文件备份到树外;(2)重置a.-u.位;(3)将文件重置为其原始内容`git checkout -- file`;(4)git pull或merge,现在将成功;(5)将文件复制回来并检查更改;(6)再次设置a.-u.位。这是我书中PITA的定义,但您的情况可能有所不同。 :) - kkm

65

我使用 git filter-branch 实现了这个功能。我使用的确切命令来自于 man 页面:

警告: 这将从您的整个历史记录中删除该文件。

git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD

这个命令将重新创建整个提交历史,在每次提交之前执行git rm以删除指定的文件。在运行命令之前不要忘记备份,因为它会被永久删除。


13
这将更改所有提交的ID,因此会破坏来自您存储库副本之外分支的合并。 - bdonlan
27
警告:此操作将从您的整个历史记录中删除该文件。虽然如此,这正是我所需要的,以删除一个完全不必要且过大的文件(输出内容本不应该提交),该文件很久以前就被提交到版本历史记录中了。 - zebediah49

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