编辑:我最近发现了git-filter-repo。这可能是一个更好的选择。自己调查 rationale 和 filter-branch gotchas 或许是个好主意,但它们不会影响我下面的用例。
这种方法使 Git 完全忘记被忽略的文件(过去/现在/未来),但不会不从工作目录中删除任何内容(甚至从远程重新拉出时)。
此方法需要在所有有文件的提交中使用/.git/info/exclude(首选)或 预先存在的 .gitignore被忽略/忘记。 1
此方法避免在下一个git pull 2
上从其他开发人员机器上删除新忽略的文件
所有 强制 Git 的方法会忽略事后行为,有效地重写历史记录,因此在此过程之后可能会拉取任何公共/共享/协作存储库的 significant ramifications。 3
一般建议:从一个干净的 repo 开始 - 一切都已提交,工作目录或索引中没有任何待处理的内容,并进行备份!
此外,this answer 的 cmets/revision history(and revision history 的 this question)可能有用/启发性。
#commit up-to-date .gitignore (if not already existing)
#these commands must be run on each branch
#these commands are not strictly necessary if you don't want/need a .gitignore file. .git/info/exclude can be used instead
git add .gitignore
git commit -m "Create .gitignore"
#apply standard git ignore behavior only to current index, not working directory (--cached)
#if this command returns nothing, ensure /.git/info/exclude AND/OR .gitignore exist
#this command must be run on each branch
#if using .git/info/exclude, it will need to be modified per branch run, if the branches have differing (per-branch) .gitignore requirements.
git ls-files -z --ignored --exclude-standard | xargs -r0 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
#optionally use the --amend flag to merge this commit with the previous one instead of creating 2 commits.
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 -r0 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
最后,关注this GitHub guide 的其余部分(从第 6 步开始)其中包括有关以下命令的重要警告/信息。
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
从现在修改的远程仓库中提取的其他开发人员应该进行备份,然后:
#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 存在于提交历史中的哪个位置,并且显然重写历史是一个非常敏感的主题,即使知道@ 987654330@.
FWIW,潜在的方法可能包括 git rebase 或 git filter-branch,它们将 external .gitignore 复制到每个提交中,例如 this question 的答案
2 通过提交独立 git rm --cached 命令的结果来强制执行 git 事后忽略行为可能会导致新忽略的文件 删除 将来从强制推送的遥控器。 git filter-branch 命令中的 --prune-empty 标志(or git reset HEAD^ 之后)通过自动删除先前的“删除所有忽略的文件”仅索引提交来避免此问题。
3 重写 git 历史记录也会更改提交哈希,这将在未来从公共/共享/协作存储库中拉取时 wreak havoc。在对这样的回购执行此操作之前,请充分了解ramifications。 This GitHub guide 指定以下内容:
告诉你的合作者rebase,不要合并他们从你旧的(受污染的)存储库历史创建的任何分支。一次合并提交可能会重新引入部分或全部您刚刚费力清除的受污染历史。
不影响远程仓库的替代解决方案是git update-index --assume-unchanged </path/file> 或git update-index --skip-worktree <file>,可以在here 找到示例。