让Git遗忘已跟踪但现被列入.gitignore的文件的方法

让Git遗忘已跟踪但现被列入.gitignore的文件的方法

技术背景

.gitignore 文件用于防止未跟踪的文件被添加到Git的跟踪文件集中,但对于已经被Git跟踪的文件,.gitignore 不会使其停止跟踪。因此,当我们将原本已被跟踪的文件添加到 .gitignore 后,需要额外的操作让Git遗忘这些文件。

实现步骤

方法一:使用 git update-index --skip-worktree

如果你想保留本地文件,但从Git跟踪中移除它,可以使用以下命令:

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

若要取消该操作,可使用:

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

方法二:使用 git rm --cached

此方法会从索引中移除文件,下次提交时该文件将从HEAD版本中移除。

  • 移除单个文件:
1
git rm --cached <file>
  • 递归移除文件夹及其所有文件:
1
git rm -r --cached <folder>

也可以通过以下一系列命令移除Git索引中的所有项,并更新索引,同时遵循 .gitignore 规则:

1
2
3
git rm -r --cached .
git add .
git commit -am "Remove ignored files"

或者使用单行命令:

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

方法三:使用 git filter-branch

此方法会从整个历史记录中删除文件,使用前请备份文件。

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

通用步骤

当需要让Git遗忘已跟踪但现被列入 .gitignore 的文件时,可按以下通用步骤操作:

  1. 提交所有更改:确保所有更改(包括 .gitignore 文件的更改)都已提交。
  2. 移除仓库中的所有跟踪文件
1
git rm -r --cached .
  1. 重新添加所有文件
1
git add .
  1. 提交更改
1
git commit -m ".gitignore fix"

最后,将更改推送到远程仓库:

1
git push

核心代码

移除单个文件示例

1
2
3
4
5
# 假设要移除 test.txt 文件
git rm --cached test.txt
git add .
git commit -m "test.txt removed"
git push

移除文件夹示例

1
2
3
4
5
# 假设要移除 .idea 文件夹
git rm -r --cached .idea
git add .
git commit -m ".idea removed"
git push

最佳实践

  • 对于配置文件等每个开发者有自己独立配置的文件,使用 git update-index --skip-worktree 可以避免远程内容覆盖本地配置。
  • 当需要从整个历史记录中删除敏感文件时,使用 git filter-branch 命令,但要注意备份文件。
  • 在执行可能影响历史记录的操作(如 git filter-branch)前,确保所有开发者都了解并做好备份。

常见问题

使用 git rm --cached 后他人拉取文件被删除

使用 git rm --cached 会导致其他开发者在执行 git pull 时,文件在他们的文件系统中被删除。可以采用以下替代方案:

  • 让应用程序查找被忽略的文件 config-overide.ini 并使用它覆盖已提交的文件 config.ini
  • 提交 config-sample.ini 文件并忽略 config.ini,必要时使用脚本复制文件。
  • 尝试使用 gitattributesclean/smudge 功能处理配置文件的更改。
  • 将配置文件放在专用的部署分支上,不合并到主分支。

文件名包含特殊字符(如换行符)时处理失败

使用以下命令可以安全处理包含特殊字符的文件名:

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

使用 git filter-branch 后远程仓库处理问题

使用 git filter-branch 更改历史记录后,需要临时允许非快进式推送,并通知其他开发者使用 rebase 而非 merge 处理分支。具体命令如下:

1
2
3
4
5
6
7
git fetch --all
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch full/path/to/file' --prune-empty --tag-name-filter cat -- --all
git push origin --force --all
git push origin --force --tags
git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --prune=now

其他开发者拉取修改后的远程仓库时,可按以下步骤操作:

1
2
git fetch --all
git reset FETCH_HEAD

让Git遗忘已跟踪但现被列入.gitignore的文件的方法
https://119291.xyz/posts/2025-05-07.make-git-forget-tracked-files-in-gitignore/
作者
ww
发布于
2025年5月7日
许可协议