504

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

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

4

9 回答 9

635

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

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

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

于 2012-11-24T13:34:35.997 回答
496

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

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在推送您的提交之前检查您的。

于 2015-12-23T11:58:30.537 回答
186

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

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

基本上,git clean -hman git-clean(在 unix 中)会给你帮助。

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

希望能帮助到你。

于 2016-04-12T12:45:48.387 回答
9

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

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

解释:

  1. 打印 .gitignore 文件
  2. 从印刷品中删除所有评论
  3. 删除所有空行
  4. 将 'git rm -r' 添加到行首
  5. 执行每一行。
于 2017-09-29T17:54:16.647 回答
3

" git clean" ( man )( man )在处理或显示被忽略目录中的被忽略路径时感到困惑,这已在 Git 2.32(2021 年第二季度)中得到纠正。git ls-files -i

这意味着接受的答案的 2021 版本将是:

git ls-files -i -c --exclude-from=.gitignore | xargs git rm --cached  
                ^^

参见Elijah Newren ( ) 的提交b548f0f、提交dd55fc0提交 aa6e1b2提交 a97c7a8提交 2e4e43a提交 b338e9f提交 7fe1ffd提交 7f9dd87(2021 年 5 月 12 日。 请参阅Derrick Stolee ( ) 的提交 4e689d8(2021 年 5 月 12 日(由Junio C Hamano 合并 -- --提交 33be431中,2021 年 5 月 20 日)newren
derrickstolee
gitster

ls-files: 除非指定了 -o 或 -c,否则 -i 会出错

签字人:以利亚·纽伦

ls-files --ignored( man )可以与--othersor一起使用--cached

在迷惑了一段时间并深入研究代码之后,我认为它ls-files -i只是坏了,没有打印任何东西,当我终于意识到-i可以用来--cached查找跟踪的忽略时,我准备好提交一个不错的补丁。

虽然这是我的错误,仔细阅读文档可能会更清楚这一点,但我怀疑这也是其他人可能犯的错误。
事实上,在我们的测试套件中的两种用途中,我相信两者中的一种确实犯了这个错误。
在 t1306.13 中,没有跟踪文件,并且在该测试和之前的测试中建立和使用的所有排除项都必须与未跟踪文件有关。
然而,由于他们正在寻找一个空的结果,这个错误并没有被注意到,因为他们的错误命令也恰好给出了一个空的答案。

-i将大部分时间与 一起使用-o,这表明我们可以在没有 a或的情况下进行-i暗示,但这将是一个向后不兼容的中断。 相反,让我们只标记没有 a或作为错误,并更新两个相关的测试用例以指定它们的意图。-o-o-c
-i-o-c

这意味着如果没有-c,您将获得(从 Git 2.32 开始,2021 年第二季度):

fatal: ls-files -i must be used with either -o or -c

注意:这仍在进行中,因为它已在 Git 2.32-rc2 中恢复,但已通过Junio C Hamano ( )的提交 2c9f1bf提交 1df046b(2021 年 5 月 27 日)修复。 请参阅Elijah Newren ( ) 的提交 906fc55(2021 年 5 月 27 日。 请参阅Derrick Stolee ( ) 的提交 eef8148(2021 年 5 月 27 日(由Junio C Hamano 合并——提交 329d63e中,2021 年 5 月 28 日)gitster
newren
derrickstolee
gitster

dir: 介绍readdir_skip_dot_and_dotdot()助手

签字人:以利亚·纽伦

于 2021-05-23T21:07:36.420 回答
1

如果您真的想修剪.gitignored 文件的历史记录,请首先保存.gitignore在 repo 之外,例如 as /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文件,它会产生“致命:不能使用 ../.gitignore 作为排除文件”(可能在git filter-branch --index-filter工作目录(认为)为空期间?)
  • 我希望使用类似的东西git ls-files -iX <(git show $(git hash-object -w .gitignore))来避免在.gitignore其他地方复制,但仅此一项就已经返回一个空字符串(而cat <(git show $(git hash-object -w .gitignore))确实.gitignore按预期打印了 's 的内容),所以我不能使用<(git show $GITIGNORE_HASH)in git filter-branch...
  • 如果您实际上只想.gitignore-clean 特定分支,--all请在最后一行替换为它的名称。那时可能无法正常工作,--tag-name-filter cat即您可能无法直接正确传输单个分支的标签
于 2020-11-23T10:03:03.113 回答
0

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

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

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

于 2019-11-26T15:21:15.863 回答
0

该解决方案添加了回车符(我是 WSL 用户,所以这很重要)和括号转义(有时这对 LaTeX 用户很重要,例如*.synctex(busy))。


斯科特解决方案的启发:

cat .gitignore | sed "s/\r//" | sed -r "/^(#.*|\s*)$/d" | sed -r "s/([()])/\\\\\1/g" | sed "s/^/git rm -r /" | bash
  1. 删除:回车 ( s/\r//)。
  2. 删除包含以下内容的行:注释 ( /^#.*$/)、空行组 ( /^\s*$/、匹配空格或空行)。注意管道|字符,这是标准的正则表达式,并且需要-r(尽管我相信-E也可以)。
  3. 替换:括号/([()])/及其转义版本\\\1\1匹配组,在这种情况下,它表示[()], or (or ), 任何匹配的内容。注意g标志,这是为了匹配(并替换)所有括号。可以重写,就"s/(\(|\))/\\\\\1/g"好像你喜欢那样。
  4. 前置git rm -r

更换的样子s/$old/$new/$flags。删除看起来像/$old/d。前置是替换/^/. 你可以通过替换来追加/$/。当然,有些字符会被转义,因为据我所知,你不能在 bash 中制作原始字符串。最后,这行可以压缩,但为了可读性,我选择将其扩展。


我看到有人质疑(在斯科特的解决方案中)这sed是直截了当的。我喜欢将这种方法视为最基本和最猴子的方法,这很好,因为如果您需要这种方法的变体,您可以当场制作。如果有的话,这是练习正则表达式的好借口。

于 2021-11-24T06:43:46.347 回答
-5

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

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

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

于 2012-11-24T13:31:16.170 回答