首页 > 解决方案 > 确定性追溯 .gitignore(如何让 Git 完全/追溯地忘记现在在 .gitignore 中的文件)

问题描述

前言

这个问题试图消除关于追溯应用 .gitignore 的困惑,而不仅仅是现在/未来。1

基本原理

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

我正在寻求的解决方案:

 *.ext
 !*special.ext

不是解决方案

git rm --cached *.ext
git commit

这需要 1. 手动指定文件和 2. 额外的提交,这将导致在被其他开发人员拉取时新忽略的文件删除。(它实际上只是一个git rm- 这是从 git 跟踪中删除- 但它将文件单独留在本地(您的)工作目录中。 之后将收到文件删除提交的其他人)git pull

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

虽然这确实会追溯清除文件,但它 1. 需要手动指定文件和 2.本地工作目录中删除指定的文件,就像普通的一样git rm(对于其他人也是如此git pull)!


脚注

1这里有许多关于 SO 的类似帖子,其中的问题定义不明确,甚至答案也不准确。看到这个问题有 23 个答案,其中根据“忘记”的标准定义,接受了 ~4k 票的答案不正确的,正如一个最正确的答案所指出的那样只有2 个答案包含所需 git filter-branch的命令。

这个有 21 个答案 问题被标记为与前一个问题的重复,但问题的定义不同(忽略与忘记),因此虽然答案可能是合适的,但它不是重复的。

这个问题是我发现的最接近我正在寻找的问题,但答案并非在所有情况下都有效(带空格的路径......),并且对于创建外部到-repository .gitignore 文件并将其复制到每个提交中。

标签: gitgitignore

解决方案


编辑:我最近发现git-filter-repo。这可能是一个更好的选择。自己调查基本原理和过滤器分支问题也许是个好主意,但它们不会影响我下面的用例。


这种方法使 Git完全忘记被忽略的文件(过去/present/future),但不会工作目录中删除任何内容(即使从远程重新拉出)。

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

此方法避免在接下来的2中从其他开发人员机器中删除新忽略的文件git pull

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

一般建议:从一个干净的 repo 开始——所有内容都已提交,工作目录或索引中没有任何待处理的内容,并进行备份

此外,此答案的评论/修订历史以及此问题的修订历史)可能有用/有启发性。

#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 指南的其余部分(从第 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,而且很明显,重写历史记录是一个非常敏感的主题,即使知道后果也是如此。

FWIW,潜在的方法可能包括git rebase或将外部git filter-branch复制到每个提交中,例如这个问题的答案 .gitignore

2通过提交独立命令的结果在事后强制执行 git 忽略行为可能会导致将来从强制推送远程拉取git rm --cached新忽略的文件删除。命令中的--prune-empty标志git filter-branch git reset HEAD^之后)通过自动删除先前的“删除所有忽略的文件”仅索引提交来避免此问题。

3重写 git 历史也会更改提交哈希,这将对公共/共享/协作存储库的未来拉取造成严重破坏。在对此类回购执行此操作之前,请充分了解其后果。此 GitHub 指南指定以下内容:

告诉你的合作者对他们从旧的(受污染的)存储库历史创建的任何分支进行rebase而不是合并。一次合并提交可能会重新引入部分或全部受污染的历史记录,而这些历史记录是您刚刚费力清除的。

影响远程 repo 的替代解决方案是git update-index --assume-unchanged </path/file>or git update-index --skip-worktree <file>,可以在此处找到示例。


推荐阅读