编辑:我最近发现git-过滤器-repo https://github.com/newren/git-filter-repo。这可能是一个更好的选择。也许调查一下是个好主意理由 https://github.com/newren/git-filter-repo#why-filter-repo-instead-of-other-alternatives和过滤器分支gotchas https://git-scm.com/docs/git-filter-branch#SAFETY对于你自己来说,但它们不会影响我下面的用例。
这个方法使得Git完全忘记被忽略的文件(past/现在/未来),但是确实not从工作目录中删除任何内容(即使从远程重新拉取)。
This method requires usage of /.git/info/exclude
(preferred) OR a pre-existing .gitignore
in all the commits that have files to be ignored/forgotten. 1
This method avoids removing the newly-ignored files from other developers machines on the next git pull
2
All methods of enforcing Git ignore behavior after-the-fact effectively re-write history and thus have significant ramifications https://stackoverflow.com/q/1491001 for any public/shared/collaborative repos that might be pulled after this process. 3
一般建议:从一个干净的仓库开始- 已提交的所有内容,工作目录或索引中没有任何待处理的内容,并进行备份!
另外,评论/修订记录 https://stackoverflow.com/posts/57454176/revisions of 这个答案 https://stackoverflow.com/a/57454176 (和修订历史 https://stackoverflow.com/posts/57418769/revisions of 这个问题 https://stackoverflow.com/posts/57418769)可能有用/有启发性。
#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
最后,按照其余的这个 GitHub 指南 https://help.github.com/en/articles/removing-sensitive-data-from-a-repository(从第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 Because /.git/info/exclude
can be applied to all historical commits using the instructions above, perhaps details about getting a .gitignore
file into the historical commit(s) that need it is beyond the scope of this answer. I wanted a proper .gitignore
to be in the root commit, as if it was the first thing I did. Others may not care since /.git/info/exclude
can accomplish the same thing regardless where the .gitignore
exists in the commit history, and clearly re-writing history is a very touchy subject, even when aware of the ramifications https://stackoverflow.com/q/1491001.
FWIW,潜在的方法可能包括git rebase
or a git filter-branch
复制一个external .gitignore
进入每个提交,就像答案一样这个问题 https://stackoverflow.com/q/43463687
2 Enforcing git ignore behavior after-the-fact by committing the results of a standalone git rm --cached
command may result in newly-ignored file deletion in future pulls from the force-pushed remote. The --prune-empty
flag in the git filter-branch
command (or git reset HEAD^
afterwards) avoids this problem by automatically removing the previous "delete all ignored files" index-only commit.
3 Re-writing git history also changes commit hashes, which will wreak havoc https://stackoverflow.com/q/1491001 on future pulls from public/shared/collaborative repos. Please understand the ramifications https://stackoverflow.com/q/1491001 fully before doing this to such a repo. This GitHub guide https://help.github.com/en/articles/removing-sensitive-data-from-a-repository specifies the following:
告诉你的合作者rebase https://git-scm.com/book/en/Git-Branching-Rebasing, not合并他们从旧的(受污染的)存储库历史记录中创建的任何分支。一次合并提交可能会重新引入您刚刚费尽心思清除的部分或全部受污染的历史记录。
替代解决方案do not影响远程仓库的是git update-index --assume-unchanged </path/file>
or git update-index --skip-worktree <file>
,其示例可以找到here https://stackoverflow.com/a/20241145.