ChatGPT解决这个技术问题 Extra ChatGPT

如何删除 .gitignore 中列出但仍在存储库中的文件?

我的存储库中有一些文件应该被忽略,我将它们添加到 .gitignore 但是,当然,它们并没有从我的存储库中删除。

所以我的问题是,是否有一个使用 filter-branch 的神奇命令或脚本可以重写我的历史记录并轻松删除所有这些文件?或者只是一个将创建将删除它们的提交的命令?

警告:虽然这不会从本地删除物理文件,但会在下一次 git pull 时从其他开发人员机器中删除文件。 How to make Git “forget” about a file that was tracked but is now in .gitignore?
@Stevoisiak 这不是那个问题的重复,因为这个问题询问所有被忽略的文件,并且它的答案也比任何类似问题都好。

H
Henrik Høyer

您可以手动将它们从存储库中删除:

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 $_}

关于在没有这些文件的情况下重写整个历史,我非常怀疑是否有一种自动的方法可以做到这一点。而且我们都知道重写历史是不好的,不是吗? :)


不幸的是,Windows 命令上的 Git Bash 不适用于包含空格的路径
@NateBundy 如果您指的是 xargs 不适用于空格这一事实,大多数命令行实用程序通过使用特殊标志来解决这个问题,这样空格就无关紧要了。我不记得 git ls-filesxargs 的标志是什么(我认为 xargs 可能是 -0),但你可以查一下。
“git ls-files -i --exclude-from=.gitignore”非常有用,它告诉我.ignore 排除了哪些文件。
我很高兴找到这些命令,但它并没有真正从存储库中删除文件。当我删除 2300 kB 的图像时,存储库大小仅下降了 10 kB。因此它不能用于,例如,使存储库更小,传输速度更快。
请注意,无论您当前的目录如何,--exclude-from= 都是相对于 git 根目录的。因此,如果您想对子目录中的 .gitignore 执行此操作,请使用 --exclude-from=[subdirectory]/.gitignore
g
gtatr

无论操作系统如何,一种更简单的方法是

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


这对 repo 上执行拉取的其他用户有何影响 - 当我读到它时,我们不想再跟踪的文件夹被删除了???我们如何防止这种情况 - 我们只是想取消追踪
假提交消息是什么意思?这是一个真正的提交消息:P 当然,您可以根据需要更改消息...
只是为了完整起见,您最后需要 git push 命令。
不要这样做,它会删除所有文件的历史记录
只是为了澄清一点,因为在执行 git rm -r --cached . git add . 之后的 git status 看起来有点可怕:@gtatr 提供的这三个命令本质上是从 git 中删除以前跟踪但后来被添加到您的 .gitignore 中的文件文件。当我第一次运行它时,我看到一堆文件并吓了一跳,但经过进一步检查,我注意到它们都是我的 .gitignore 文件中列出的所有文件。
B
Brian Topping

由于 .gitignore 中的文件没有被跟踪,您可以使用 git clean 命令递归删除不受版本控制的文件。

使用 git clean -xdn 执行试运行并查看将删除的内容。
然后使用 git clean -xdf 执行它。

基本上,git clean -hman git-clean(在 unix 中)将为您提供帮助。

请注意,此命令还将删除不在暂存区域中的新文件。

希望能帮助到你。


这应该是公认的答案。它确实有效(在 macOS 上,接受的答案对我不起作用),而且更干净。
此答案不适用 - OP 表示 .gitignore 中的文件正在被跟踪。
谨防!这将永久删除所有未跟踪的文件,而不仅仅是从分支中删除
git clean -xdn 是不会删除的试运行。下一个会。
-1:这是一个高度误导的答案 - 原始发布者想要从存储库中删除,而不是完全删除文件。我已经接近删除我的 IDE 所需但不需要在 repo 中的大量动态文件。
S
Scott Crossen

通过使用 sed 操作 .gitignore 语句的输出,我做了一个非常简单的解决方案:

cat .gitignore | sed '/^#.*/ d' | sed '/^\s*$/ d' | sed 's/^/git rm -r /' | bash

解释:

打印 .gitignore 文件从打印中删除所有注释删除所有空行在行首添加 'git rm -r' 执行每一行。


sed 是直截了当的?
另一个问题的类似示例:stackoverflow.com/a/63534476/7127519
+1 用于编写脚本和解释它。解释在没有审查的情况下将其直接运行到 bash 中的危险可能是一个疏忽。
V
VonC

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 b548f0fcommit dd55fc0commit aa6e1b2commit a97c7a8commit 2e4e43acommit b338e9fcommit 7fe1ffdcommit 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 2c9f1bfcommit 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


T
Tobias Kienzler

如果您真的想删除 .gitignored 文件的历史记录,请先将 .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 可能无法正常工作,即您可能无法直接正确传输单个分支的标签


A
Alex Styl

在 linux 中,您可以使用以下命令:

例如我想删除 *.py~ 所以我的命令应该是 ==>

find . -name "*.py~" -exec rm -f {} \;


J
Jaacko Torus

该解决方案添加了回车符(我是 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 是直截了当的。我喜欢将这种方法视为最基本和最猴子的方法,这很好,因为如果您需要这种方法的变体,您可以当场制作。如果有的话,这是练习正则表达式的好借口。


p
pensz

将 .gitignore 模式添加到 .gitignore 后,git 将忽略匹配的文件。

但是存储库中已经存在的文件仍然存在。

使用 git rm files_ignored; git commit -m 'rm no use files' 删除忽略的文件。


它们有很多,有没有办法删除它们而不必指定它们的名字?