明确的追溯 .gitignore (如何让 Git 完全/追溯地*忘记*现在在 .gitignore 中的文件)

2024-04-27

Preface

This question attempts to clear the confusion regarding applying .gitignore retroactively, not just to the present/future.1

基本原理

我一直在寻找一种方法来追溯执行我当前的 .gitignore,就好像我在第一次提交中创建了 .gitignore.

我正在寻求的解决方案:

  • Will not需要手动指定文件
  • Will not需要提交
  • 将追溯适用于所有分支的所有提交
  • Will ignore工作目录中.gitignore指定的文件,不删除它们(就像最初由 root 提交的 .gitignore 文件一样)
  • 将使用 git,而不是 BFG
  • 将适用于 .gitignore 异常,例如:
 *.ext
 !*special.ext

不是解决方案

git rm --cached *.ext
git commit

这需要 1. 手动指定文件和 2. 额外提交,这将导致新忽略的文件deletion当被其他开发者拉动时。 (这实际上只是一个git rm- 这是一个deletion来自 git 跟踪 - 但它将文件单独留在本地(your) 工作目录。Others who git pull之后将收到文件deletion commit)

git filter-branch --index-filter 'git rm --cached *.ext'

虽然这does追溯清除文件,它 1. 需要手动指定文件,2.deletes指定的文件来自local工作目录 https://stackoverflow.com/a/57419809/423125就像普通的一样git rm(对于其他人来说也是如此git pull)!


脚注

1There are many similar posts here on SO, with less-than-specifically-defined questions and even more less-than-accurate answers. See this question with 23 answers https://stackoverflow.com/q/1274057 where the accepted answer with ~4k votes https://stackoverflow.com/a/1274447 is incorrect according to the standard definition of "forget" as noted by one mostly-correct answer https://stackoverflow.com/a/56051380, and only https://stackoverflow.com/a/1274126/423125 2 answers https://stackoverflow.com/a/56051380/423125 include the required git filter-branch command.

这个问题有21个答案 https://stackoverflow.com/q/936249/423125is 被标记为与前一个问题重复,但问题的定义不同(忽略与忘记),因此虽然答案可能是合适的,但它是not重复的。

这个问题 https://stackoverflow.com/q/43463687是我发现的最接近我正在寻找的内容,但答案并不适用于所有情况(带空格的路径...),并且对于创建外部存储库来说可能比必要的更复杂.gitignore 文件并将其复制到每个提交中。


编辑:我最近发现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.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

明确的追溯 .gitignore (如何让 Git 完全/追溯地*忘记*现在在 .gitignore 中的文件) 的相关文章

随机推荐