我的存储库中有一些文件应该被忽略,我将它们添加到 .gitignore 但是,当然,它们并没有从我的存储库中删除。
所以我的问题是,是否有一个使用 filter-branch 的神奇命令或脚本可以重写我的历史记录并轻松删除所有这些文件?或者只是一个将创建将删除它们的提交的命令?
您可以手动将它们从存储库中删除:
git rm --cached file1 file2 dir/file3
或者,如果您有很多文件:
git rm --cached `git ls-files -i -c --exclude-from=.gitignore`
但这似乎不适用于 Windows 上的 Git Bash。它会产生一条错误消息。以下效果更好:
git ls-files -i -c --exclude-from=.gitignore | xargs git rm --cached
在 Windows 上的 PowerShell 中,这效果更好(处理路径和文件名中的空格):
git ls-files -i -c --exclude-from=.gitignore | %{git rm --cached $_}
关于在没有这些文件的情况下重写整个历史,我非常怀疑是否有一种自动的方法可以做到这一点。而且我们都知道重写历史是不好的,不是吗? :)
无论操作系统如何,一种更简单的方法是
git rm -r --cached .
git add .
git commit -m "Drop files from .gitignore"
您基本上删除并重新添加所有文件,但 git add
将忽略 .gitignore
中的文件。
使用 --cached
选项会将文件保留在文件系统中,因此您不会从磁盘中删除文件。
注意:有些人在评论中指出您将丢失所有文件的历史记录。我在 MacOS 上使用 git 2.27.0 对此进行了测试,但事实并非如此。如果您想检查发生了什么,请在推送您的提交之前检查您的 git diff HEAD~1
。
git rm -r --cached .
git add .
之后的 git status
看起来有点可怕:@gtatr 提供的这三个命令本质上是从 git 中删除以前跟踪但后来被添加到您的 .gitignore 中的文件文件。当我第一次运行它时,我看到一堆文件并吓了一跳,但经过进一步检查,我注意到它们都是我的 .gitignore 文件中列出的所有文件。
由于 .gitignore 中的文件没有被跟踪,您可以使用 git clean 命令递归删除不受版本控制的文件。
使用 git clean -xdn
执行试运行并查看将删除的内容。
然后使用 git clean -xdf
执行它。
基本上,git clean -h
或 man git-clean
(在 unix 中)将为您提供帮助。
请注意,此命令还将删除不在暂存区域中的新文件。
希望能帮助到你。
.gitignore
中的文件正在被跟踪。
git clean -xdn
是不会删除的试运行。下一个会。
通过使用 sed 操作 .gitignore 语句的输出,我做了一个非常简单的解决方案:
cat .gitignore | sed '/^#.*/ d' | sed '/^\s*$/ d' | sed 's/^/git rm -r /' | bash
解释:
打印 .gitignore 文件从打印中删除所有注释删除所有空行在行首添加 'git rm -r' 执行每一行。
sed
是直截了当的?
“git clean
”(man) 和 git ls-files -i
(man) 在处理或显示被忽略目录中的被忽略路径时存在混淆,该目录已被用 Git 2.32(2021 年第二季度)更正。
这意味着 2021 版的 the accepted answer 将是:
git ls-files -i -c --exclude-from=.gitignore | xargs git rm --cached
^^
请参阅 Elijah Newren (newren
) 的 commit b548f0f、commit dd55fc0、commit aa6e1b2、commit a97c7a8、commit 2e4e43a、commit b338e9f、commit 7fe1ffd、commit 7f9dd87(2021 年 5 月 12 日)。
请参阅 commit 4e689d8( 2021 年 5 月 12 日)由 Derrick Stolee (derrickstolee
) 提供。
(由 commit 33be431 中的 Junio C Hamano -- gitster
-- 合并,2021 年 5 月 20 日)
ls-files:-i 出错,除非指定了 -o 或 -c 签名人:Elijah Newren
ls-files --ignored(man) 可以与 --others 或 --cached 一起使用。在有点困惑并深入研究代码之后,我认为 ls-files -i 只是坏了,没有打印任何东西,当我终于意识到 -i 可以与 --缓存以查找跟踪的忽略。虽然这是我的错误,仔细阅读文档可能会更清楚这一点,但我怀疑这也是其他人可能犯的错误。事实上,在我们的测试套件中的两种用途中,我相信两者中的一种确实犯了这个错误。在 t1306.13 中,没有跟踪文件,并且在该测试和之前的测试中建立和使用的所有排除项都必须与未跟踪文件有关。然而,由于他们正在寻找一个空的结果,这个错误并没有被注意到,因为他们的错误命令也恰好给出了一个空的答案。 -i 将大部分时间与 -o 一起使用,这表明我们可以在没有 -o 或 -c 的情况下使 -i 隐含 -o,但这将是向后不兼容的中断。相反,让我们将 -i 标记为错误,不带 -o 或 -c,并更新两个相关的测试用例以指定它们的意图。
这意味着如果没有 -c
,您将获得(从 Git 2.32 开始,2021 年第二季度):
fatal: ls-files -i must be used with either -o or -c
注意:这项工作仍在进行中,因为它是 reverted in Git 2.32-rc2,但由 Junio C Hamano (gitster
) 用 commit 2c9f1bf、commit 1df046b(2021 年 5 月 27 日)修复。
参见 commit 906fc55(2021 年 5 月 27 日)由 { 6}.
参见 Derrick Stolee (derrickstolee
) 的commit eef8148(2021 年 5 月 27 日)。
(由 Junio C Hamano -- gitster
-- 在 commit 329d63e 中合并,2021 年 5 月 28 日)
dir: 介绍 readdir_skip_dot_and_dotdot() helper 签名者: Elijah Newren
如果您真的想删除 .gitignore
d 文件的历史记录,请先将 .gitignore
保存在存储库之外,例如保存为 /tmp/.gitignore
,然后运行
git filter-branch --force --index-filter \
"git ls-files -i -X /tmp/.gitignore | xargs -r git rm --cached --ignore-unmatch -rf" \
--prune-empty --tag-name-filter cat -- --all
笔记:
我认为 git filter-branch --index-filter 在 .git 目录中运行,即如果你想使用相对路径,你必须先添加一个 ../ 。显然你不能使用 ../.gitignore,实际的 .gitignore 文件,由于某种原因(可能在 git filter-branch --index-过滤工作目录(认为)为空?)
我希望使用 git ls-files -iX <(git show $(git hash-object -w .gitignore)) 之类的东西来避免将 .gitignore 复制到其他地方,但仅此一项就已经返回一个空字符串(而 cat < (git show $(git hash-object -w .gitignore)) 确实按预期打印了 .gitignore 的内容),所以我不能在 git filter-branch 中使用 <(git show $GITIGNORE_HASH) ...
如果您实际上只想 .gitignore-clean 特定分支,请将最后一行中的 --all 替换为其名称。 --tag-name-filter cat 可能无法正常工作,即您可能无法直接正确传输单个分支的标签
在 linux 中,您可以使用以下命令:
例如我想删除 *.py~
所以我的命令应该是 ==>
find . -name "*.py~" -exec rm -f {} \;
该解决方案添加了回车符(我是 WSL 用户,所以这很重要)和括号转义(有时这对 LaTeX 用户很重要,例如 *.synctex(busy)
)。
受 Scott's solution 启发:
cat .gitignore | sed "s/\r//" | sed -r "/^(#.*|\s*)$/d" | sed -r "s/([()])/\\\\\1/g" | sed "s/^/git rm -r /" | bash
删除:回车(s/\r//)。删除包含以下内容的行:注释(/^#.*$/)、空行组(/^\s*$/,匹配空格或空行)。注意管道 |字符,这是标准的正则表达式,需要 -r (虽然我相信 -E 也可以)。替换:括号 /([()])/ 及其转义版本 \\\1,\1 匹配组,在这种情况下,它表示 [()] 或 ( 或 ),无论匹配什么。注意 g 标志,这是匹配(并替换)所有括号。如果你喜欢,可以重写为 "s/(\(|\))/\\\\\1/g"。前置 git rm -r
替换看起来像 s/$old/$new/$flags
。删除看起来像 /$old/d
。 Prepending 正在替换 /^/
。您可以通过替换 /$/
来进行追加。当然,有些字符会被转义,因为据我所知,你不能在 bash 中制作原始字符串。最后,这行可以压缩,但为了可读性,我选择将其扩展。
我看到有人质疑(在 Scott 的解决方案中)sed
是直截了当的。我喜欢将这种方法视为最基本和最猴子的方法,这很好,因为如果您需要这种方法的变体,您可以当场制作。如果有的话,这是练习正则表达式的好借口。
将 .gitignore 模式添加到 .gitignore 后,git 将忽略匹配的文件。
但是存储库中已经存在的文件仍然存在。
使用 git rm files_ignored; git commit -m 'rm no use files'
删除忽略的文件。
xargs
不适用于空格这一事实,大多数命令行实用程序通过使用特殊标志来解决这个问题,这样空格就无关紧要了。我不记得git ls-files
和xargs
的标志是什么(我认为xargs
可能是-0
),但你可以查一下。--exclude-from=
都是相对于 git 根目录的。因此,如果您想对子目录中的 .gitignore 执行此操作,请使用--exclude-from=[subdirectory]/.gitignore
。