如何让Git忘记已跟踪但现在位于. gitignore中的文件?

我将之前被Git跟踪的文件放在.gitignore列表中。但是,该文件在编辑后仍然显示在git status中。如何强制Git完全忘记该文件?

1984066 次浏览

把它拿出来,提交,然后把它搬回来。

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

将文件移动或复制到安全位置,以免丢失。然后“git rm”文件并提交。

如果您恢复到之前的提交之一,或未删除的另一个分支,该文件仍会显示。但是,在以后的所有提交中,您将不会再次看到该文件。如果该文件在Git忽略中,那么您可以将其移回文件夹,Git不会看到它。

我通过使用git过滤分支完成了这一点。我使用的确切命令来自手册页:

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

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

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

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

要停止跟踪文件,我们必须将其从索引中删除:

git rm --cached <file>

要递归删除文件夹和文件夹中的所有文件:

git rm -r --cached <folder>

从head版本中删除文件将在下一次提交时发生。

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

如果您无法git rm跟踪文件,因为其他人可能需要它(警告,即使git rm --cached,当其他人获得此更改时,他们的文件将在其文件系统中被删除)。这些通常是由于配置文件覆盖,身份验证凭据等而完成的。请查看https://gist.github.com/1423106以了解人们解决问题的方法。

总结如下:

  • 让您的应用程序查找被忽略的文件config-overide.ini并在提交的文件config.ini上使用它(或者,查找~/. config/myapp.ini或$MYCONFIGFILE)
  • 提交文件config-sample.ini并忽略文件config.ini,必要时使用脚本或类似的复制文件。
  • 尝试使用git属性清理/涂抹魔法来为您应用和删除更改,例如涂抹配置文件作为备用分支的签出,并将配置文件清理为HEAD的签出。这是很棘手的事情,我不推荐给新手用户。
  • 将配置文件保存在专用于它的部署分支上,该分支永远不会合并到master。当你想部署/编译/测试时,你可以合并到该分支并获取该文件。这本质上是涂抹/清理方法,除了使用人工合并策略和额外的git模块。
  • 反推荐:不要使用假设不变,它只会以眼泪结束(因为让git对自己撒谎会导致坏事发生,比如你的改变永远丢失)。

下面的一系列命令将从Git索引中删除所有项目(而不是从工作目录或本地存储库中删除),然后将更新Git索引,同时尊重Git忽略。PS. Index=缓存

第一:

git rm -r --cached .git add .

然后:

git commit -am "Remove ignored files"

或者作为一个单行:

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

#0做我的工作:

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

备注:此解决方案实际上独立于.gitignore,因为gitignore仅适用于未跟踪的文件。


更新,更好的选择

自从这个答案被发布以来,已经创建了一个新的选项,应该是首选的。你应该使用--skip-worktree,这是用于修改用户不想再提交的跟踪文件,并保留--assume-unchanged的性能,以防止git检查大跟踪文件的状态。有关更多详细信息,请参阅https://stackoverflow.com/a/13631525/717372

git update-index --skip-worktree <file>

取消

git update-index --no-skip-worktree <file>

来自Matt Frear的回复是最有效的IMHO。以下只是一个PowerShell脚本,供Windows上的用户仅从其Git存储库中删除与其排除列表匹配的文件。

# Get files matching exclusionsfrom .gitignore# Excluding comments and empty lines$ignoreFiles =  gc .gitignore | ?{$_ -notmatch  "#"} |  ?{$_ -match  "\S"} | % {$ignore = "*" + $_ + "*"(gci -r -i $ignore).FullName}$ignoreFiles = $ignoreFiles| ?{$_ -match  "\S"}
# Remove each of these file from Git$ignoreFiles | % { git rm $_}
git add .
git ls-files -c --ignored --exclude-standard -z | xargs -0 git rm --cachedgit commit -am "Remove ignored files"

这将获取被忽略文件的列表,从索引中删除它们,并提交更改。

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

git ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm -r --cached

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

什么对我不起作用

(在Linux下),我想使用这里建议ls-files --ignored --exclude-standard | xargs git rm -r --cached方法的帖子。然而,(一些)要删除的文件的名称中嵌入了换行符/LF/\n。两种解决方案都不是:

git ls-files --ignored --exclude-standard | xargs -d"\n" git rm --cachedgit ls-files --ignored --exclude-standard | sed 's/.*/"&"/' | xargs git rm -r --cached

处理这种情况(获取有关未找到文件的错误)。

所以我提议

git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cachedgit commit -am "Remove ignored files"

这使用ls-文件-z参数和xargs-0参数来安全/正确地处理文件名中的“讨厌”字符。

在手册页g it-ls-files(1)中,它指出:

当不使用-z选项时,选项卡、LF和反斜杠字符路径名分别表示为\t、\n和\\。

所以我认为如果文件名中包含任何这些字符,我的解决方案是必要的。

  1. 更新您的.gitignore文件-例如,将您不想跟踪的文件夹添加到.gitignore

  2. git rm -r --cached .-删除所有跟踪的文件,包括需要和不需要的文件。只要您保存在本地,您的代码就会安全。

  3. git add .-所有文件都将被添加回,除了.gitignore中的文件。


帽子提示@AkiraYamamoto为我们指明了正确的方向。

我想,也许Git不能完全忘记一个文件,因为它的概念(部分“快照,而不是差异”)。

例如,当使用CVS时,这个问题就不存在了。CVS将信息存储为基于文件的更改列表。CVS的信息是一组文件以及随着时间的推移对每个文件所做的更改。

但是在Git中,每次你提交或保存项目状态时,它基本上都会拍摄一张您的所有文件在那一刻的样子,并存储对该快照的引用。所以,如果你添加了一次文件,它将永远存在于该快照中。

这两篇文章对我很有帮助:

假设不变与跳过工作树的比较如何使用Git忽略跟踪文件中的更改

基于此,如果文件已被跟踪,我将执行以下操作:

git update-index --skip-worktree <file>

从这一刻起,此文件中的所有本地更改都将被忽略,不会转到远程。如果在远程更改文件,当git pull时将发生冲突。Stash不起作用。要解决它,请将文件内容复制到安全位置并按照以下步骤操作:

git update-index --no-skip-worktree <file>git stashgit pull

文件内容将被远程内容替换。将您的更改从安全位置粘贴到文件中,然后再次执行:

git update-index --skip-worktree <file>

如果使用项目的每个人都将执行git update-index --skip-worktree <file>,则应该不存在pull的问题。当每个开发人员都有自己的项目配置时,此解决方案适用于配置文件。

当文件在远程更改时,每次这样做都不是很方便,但它可以保护它不被远程内容覆盖。

BFG是专门为从Git存储库中删除不需要的数据(如大文件或密码)而设计的,因此它有一个简单的标志,可以删除任何大型历史(不在您当前提交的文件中)文件:

java -jar bfg.jar --strip-blobs-bigger-than 100M

如果您想按名称指定文件,您也可以这样做:

java -jar bfg.jar --delete-files *.mp4

BFG比git filter-branch快10-1000倍,通常更容易使用-检查完整的使用说明示例了解更多详细信息。

来源:减少仓库大小

如果您不想使用CLI并且在Windows上工作,一个非常简单的解决方案是使用TortoiseGit。它在菜单中具有“删除(保持本地)”操作,可以正常工作。

在以下情况下使用:

  1. 您想取消跟踪大量文件,或
  2. 你更新了你的. gitignore文件

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

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

第1步:提交所有更改

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

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

要清除您的存储库,请使用:

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

rm命令可能是无情的。如果您想事先尝试它的作用,请添加-n--dry-run标志来测试一下。

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

git add .

第4步:提交

git commit -m ".gitignore fix"

你的仓库很干净:)

将更改推送到您的遥控器,以查看在那里有效的更改。

如果已经提交DS_Store

find . -name .DS_Store -print0 | xargs -0 git rm --ignore-unmatch

忽略它们:

echo ".DS_Store" >> ~/.gitignore_globalecho "._.DS_Store" >> ~/.gitignore_globalecho "**/.DS_Store" >> ~/.gitignore_globalecho "**/._.DS_Store" >> ~/.gitignore_globalgit config --global core.excludesfile ~/.gitignore_global

最后,做出承诺!

这在最新的Git中不再是一个问题(在撰写本文时v2.17.1)。

. gitignore文件最终忽略了跟踪但删除的文件。您可以通过运行以下脚本自行测试这一点。最后的git status语句应报告“无提交”。

# Create an empty repositorymkdir gitignore-testcd gitignore-testgit init
# Create a file and commit itecho "hello" > filegit add filegit commit -m initial
# Add the file to gitignore and commitecho "file" > .gitignoregit add .gitignoregit commit -m gitignore
# Remove the file and commitgit rm filegit commit -m "removed file"
# Reintroduce the file and check status.# .gitignore is now respected - status reports "nothing to commit".echo "hello" > filegit status

我喜欢JonBrave的回答,但我有足够混乱的工作目录,commit -a让我有点害怕,所以这是我所做的:

git config --global alias.exclude-ignored '!git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cached &&  git ls-files -z --ignored --exclude-standard | xargs -0 git stage &&  git stage .gitignore && git commit -m "new gitignore and remove ignored files from index"'

分解它:

git ls-files -z --ignored --exclude-standard | xargs -0 git rm -r --cachedgit ls-files -z --ignored --exclude-standard | xargs -0 git stagegit stage .gitignoregit commit -m "new gitignore and remove ignored files from index"
  • 从索引中删除忽略的文件
  • 阶段. gitignore和您刚刚删除的文件
  • 提交

连续做以下步骤,你会没事的。

  1. 删除错误添加的文件从目录/存储。您可以使用“rm-r”(用于Linux)命令或通过浏览目录删除它们。或将它们移动到PC上的另一个位置。(如果运行移动/移除,您可能需要关闭IDE。)

  2. 现在将文件/目录添加到. gitignore文件并保存它。

  3. 现在使用这些命令从git缓存删除它们(如果有多个目录,请通过重复发出此命令逐个删除它们)

     git rm -r --cached path-to-those-files
  4. 现在使用以下命令 a提交并推送。这将从Git远程删除这些文件并使Git停止跟踪成为这些文件。

     git add .git commit -m "removed unnecessary files from Git"git push origin

复制/粘贴的答案是:

git rm --cached -r .; git add .; git status; git commit -m "Ignore unwanted files"

此命令不会更改.gitignore文件的内容。它只会忽略已经提交到Git存储库但现在我们已将它们添加到.gitignore的文件。

命令git status;只是为了查看更改,可以删除。

最后,它将立即使用消息“忽略不需要的文件”提交更改。

如果您不想提交更改,请删除命令的最后一部分(git commit -m "Ignore unwanted files"

特别是对于基于IDE的文件,我使用这个:

例如,对于slnx.sqlite文件,我完全摆脱了它,如下所示:

git rm {PATH_OF_THE_FILE}/slnx.sqlite -fgit commit -m "remove slnx.sqlite"

请记住,其中一些文件存储了一些本地用户设置和项目的首选项(例如您打开的文件)。因此,每次您在IDE中导航或进行一些更改时,该文件都会更改,因此它会将其签出并显示为未提交的更改。

使用git rm --cached命令并不能回答原来的问题:

你如何迫使git完全忘记[文件]?

事实上,当执行git pull

强制Git忘记文件的正确方法由GitHub这里记录。

我建议阅读留档,但基本上:

git fetch --allgit filter-branch --force --index-filter 'git rm --cached --ignore-unmatch full/path/to/file' --prune-empty --tag-name-filter cat -- --allgit push origin --force --allgit push origin --force --tagsgit for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdingit reflog expire --expire=now --allgit gc --prune=now

只需将full/path/to/file替换为文件的完整路径。确保您已将文件添加到.gitignore文件中。

您还需要(暂时)允许非快进推送到您的存储库,因为您正在更改您的Git历史记录。

接受的答案不是“让Git“忘记”成为一个文件……”(历史上)。它只使Git忽略成为现在/未来的文件。

此方法使Git完全忘记忽略文件(过去/现在/未来),但它确实没有从工作目录中删除任何内容(即使从远程重新拉取)。

此方法需要在所有中使用文件/.git/info/exclude(首选)预先存在.gitignore来提交要忽略/忘记的文件。1

所有强制Git的方法都忽略了事后行为,有效地重写了历史记录,因此在此过程后可能被拉取的任何公共/共享/协作存储库都有重大影响2

一般建议:从一个干净的存储库开始-提交的所有内容,工作目录或索引中没有任何未决内容,再做个备份

此外,这个答案的注释/修订历史这个问题和修订历史)可能是有用的/有启发性的。

#Commit up-to-date .gitignore (if not already existing)#This command must be run on each branch
git add .gitignoregit commit -m "Create .gitignore"
#Apply standard Git ignore behavior only to the current index, not the working directory (--cached)#If this command returns nothing, ensure /.git/info/exclude AND/OR .gitignore exist#This command must be run on each branch
git ls-files -z --ignored --exclude-standard | xargs -0 git rm --cached
#Commit to prevent working directory data loss!#This commit will be automatically deleted by the --prune-empty flag in the following command#This command must be run on each branch
git commit -m "ignored index"
#Apply standard git ignore behavior RETROACTIVELY to all commits from all branches (--all)#This step WILL delete ignored files from working directory UNLESS they have been dereferenced from the index by the commit above#This step will also delete any "empty" commits.  If deliberate "empty" commits should be kept, remove --prune-empty and instead run git reset HEAD^ immediately after this command
git filter-branch --tree-filter 'git ls-files -z --ignored --exclude-standard | xargs -0 git rm -f --ignore-unmatch' --prune-empty --tag-name-filter cat -- --all
#List all still-existing files that are now ignored properly#If this command returns nothing, it's time to restore from backup and start over#This command must be run on each branch
git ls-files --other --ignored --exclude-standard

最后,按照这个github指南的其余部分(从步骤6开始)其中包括有关以下命令的重要警告/信息

git push origin --force --allgit push origin --force --tagsgit for-each-ref --format="delete %(refname)" refs/original | git update-ref --stdingit reflog expire --expire=now --allgit gc --prune=now

从现已修改的远程存储库中提取的其他开发人员应该进行备份,然后:

#fetch modified remote
git fetch --all
#"Pull" changes WITHOUT deleting newly-ignored files from working directory#This will overwrite local tracked files with remote - ensure any local modifications are backed-up/stashed
git reset FETCH_HEAD

脚注

1因为/.git/info/exclude可以使用上面的说明应用于所有历史提交,也许关于获得.gitignore文件需要它的历史提交的细节超出了这个答案的范围。我想要一个合适的.gitignore文件在根提交中,就好像这是我做的第一件事一样。其他人可能不在乎,因为/.git/info/exclude可以完成同样的事情,无论.gitignore文件存在于提交历史中的哪里,显然重写历史是一个非常敏感的主题,即使知道后果

FWIW,潜在的方法可能包括git rebasegit filter-branch,将外部.gitignore复制到每个提交中,就像这个问题的答案一样。

2通过提交独立的git rm --cached命令的结果来强制执行Git忽略行为,可能会导致将来从强制推送的远程提取新忽略的文件删除。以下git filter-branch命令中的--prune-empty标志通过自动删除之前的“删除所有忽略的文件”仅索引提交来避免此问题。重写Git历史记录还会更改提交哈希,这将在将来从公共/共享/协作存储库中提取时造成严重破坏。在对这样的存储库执行此操作之前,请完全理解后果这个github指南指定以下内容:

告诉你的合作者rebase没有合并,他们从你的旧(受污染的)存储库历史中创建的任何分支。一次合并提交可能会重新引入你刚刚费力清除的部分或全部受污染的历史。

不要影响远程存储库的替代解决方案是git update-index --assume-unchanged </path/file>git update-index --skip-worktree <file>,可以找到这里的示例。

如果有人在Windows上遇到困难并且您想忽略整个文件夹,请转到文件资源管理器上所需的“文件夹”,右键单击并执行“Git Bash here”(应该已安装Git for Windows)。

运行此命令:

git ls-files -z | xargs -0 git update-index --assume-unchanged

在我这里的例子中,我需要删除几个目录中的几个. lock文件。我运行了以下操作,它无需进入每个目录即可删除它们:

git rm -r --cached **/*.lock

这样做会进入我所在的“根”下的每个文件夹,并排除所有与模式匹配的文件。

对文件/文件夹执行以下步骤:

删除文件:

  1. 需要将该文件添加到. gitignore。
  2. 需要使用命令(git rm--缓存文件名)删除该文件。
  3. 需要运行(git add)。
  4. 需要(提交)“删除文件”。
  5. 最后,(git推送)。

例如:

我想删除test.txt文件。我不小心推送到GitHub并想删除它。命令如下:

首先,在文件. gitignore中添加“test.txt”

git rm --cached test.txtgit add .git commit -m "test.txt removed"git push

删除文件夹:

  1. 需要将该文件夹添加到文件. gitignore
  2. 需要使用命令(git rm-r--缓存文件夹名称)删除该文件夹。
  3. 需要运行(git add)。
  4. 需要(提交)“文件夹已删除”。
  5. 最后,(git推送)。

例如:

我想删除.的想法文件夹/目录。我不小心推送到GitHub并想删除它。命令如下:

首先,在文件. gitignore中添加.想法

git rm -r --cached .ideagit add .git commit -m ".idea removed"git push

我是这样解决我的问题的:

git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD
git push

在这种情况下,我们基本上也试图在以前的提交中重写该特定文件的历史记录。

有关更多信息,您可以参考filter分支这里手册页

来源:从存储库中删除敏感数据-使用filter分支

来源:Git:如何删除一个错误提交的大文件

在我的情况下,我需要将“.envrc”放在. gitignore文件中。

然后我使用:

git update-index --skip-worktree .envrcgit rm --cached .envrc

文件被删除了。

然后我再次提交,告诉文件已被删除。

但是当我使用命令git log -p时,文件的内容(这是Amazon S3的秘密凭据)显示了被删除的内容,我不想在Git存储库的历史记录中显示此内容。

然后我使用了这个命令:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch .envrc' HEAD

我再也看不到内容了。

如果您使用echo node_modules >> .gitignore之类的命令创建gitignore文件,它将无法工作。

Windows终端将文件保存在UCS-2 LE BOM中,而git似乎不接受这一点。

您可以通过打开Notepad并使用UTF-8编码重新保存来解决此问题

记事本保存utf-8编码

它现在起作用了。

我认为他们需要解决这个问题,因为做echo "filetoignore" >> .gitignore实际上看起来很方便

git rm --cached -r <YOUR_files_or_folders>

仅从索引中删除文件

我知道这个答案已经晚了很多年,但是我决定在注意到根据用户的期望输出可能会有不同的答案之后做出贡献。

你如何迫使Git完全忘记它?

这取决于以下哪一项是期望的结果:

  1. 停止跟踪文件并将其从存储库中删除,或
  2. 停止跟踪文件而不将其从存储库中删除(可能供其他合作者下载)

对于(1),解决方案类似于上面的马特的回答。对于(2),请改为引用康斯坦丁的回答

来源:https://stackabuse.com/git-stop-tracking-file-after-adding-to-gitignore/

如果这些问题都不适合您,您需要确认您的“. gitignore”文件实际上是. gitignore文件,而不是文本文件、JSON文件等。

确保它看起来像这样。

输入图片描述输入图片描述

对我来说,该文件在历史记录中仍然可用,我首先需要压缩添加已删除文件的提交:https://gist.github.com/patik/b8a9dc5cd356f9f6f980

  1. 合并提交。下面的示例结合了最后3次提交
git reset --soft HEAD~3git commit -m "New message for the combined commit"
  1. 按下压缩的提交如果提交已被推送到远程:
git push origin +name-of-branch