我在代码仓库中有一些不需要的文件,我把它们加入到了 .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 $_}
关于没有这些文件重写整个历史,我非常怀疑是否有一种自动的方式可以做到。
而我们都知道,重写历史是不好的,对吧? :)
xargs
无法处理空格的问题,大多数命令行工具都会通过使用特殊标志来解决这个问题,使空格不再成为问题。我记不清git ls-files
和xargs
的标志是什么(我想xargs
可能是-0
),但你可以查一下。 - user456814--exclude-from=
都相对于git根目录。因此,如果您想在子目录中使用.gitignore
,请使用--exclude-from=[subdirectory]/.gitignore
。 - marcvangendgit 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文件中的文件。 - Logan Besecker由于在.gitignore文件中的文件未被跟踪,您可以使用git clean命令递归删除不在版本控制下的文件。
使用git clean -xdn
进行干扰运行以查看将要被删除的内容。
然后使用git clean -xdf
来执行它。
基本上,git clean -h
或man git-clean
(在Unix上)会给您帮助。
请注意,此命令还将删除不在暂存区中的新文件。
.gitignore
中的文件正在被跟踪。 - Ken Williamsgit clean -xdn
是一个干运行命令,不会删除任何文件。下一个命令才会真正执行删除操作。 - JohnZaj我通过使用sed操作.gitignore语句的输出来实现了一个非常简单的解决方案:
cat .gitignore | sed '/^#.*/ d' | sed '/^\s*$/ d' | sed 's/^/git rm -r /' | bash
说明:
sed
直接明了? - IgorGanapolskygit rm --cached -r .
是删除所有缓存的命令,可以递归执行。
git add .
是将所有未被.gitignore忽略的文件添加到版本控制中。
你需要提交一些已经在文件系统中删除但没有真正删除的文件。
使用下面这个命令可以一次性完成以上两个操作:git rm --cached -r . && git add .
"git clean
"(man) 和 git ls-files -i
(man) 在处理或显示被忽略目录中的被忽略路径时存在困惑,这在 Git 2.32 (Q2 2021) 中得到了纠正。
这意味着 接受的答案 的 2021 版本将是:
git ls-files -i -c --exclude-from=.gitignore | xargs git rm --cached
^^
请查看提交 b548f0f, 提交 dd55fc0, 提交 aa6e1b2, 提交 a97c7a8, 提交 2e4e43a, 提交 b338e9f, 提交 7fe1ffd, 提交 7f9dd87 (2021年5月12日),作者是Elijah Newren (newren
)。
请查看提交 4e689d8 (2021年5月12日),作者是Derrick Stolee (derrickstolee
)。
(由Junio C Hamano -- gitster
--在提交 33be431中合并)
ls-files
:未指定 -o 或 -c 时出现 -i 错误签署者:Elijah Newren
这意味着如果没有
ls-files --ignored
(man)可以与--others
或--cached
一起使用。在一段时间的困惑和代码挖掘后,我认为
ls-files -i
只是坏掉了,没有输出任何东西,当我最终意识到-i
可以与--cached
一起使用来查找已跟踪的忽略文件时,我已经准备好提交一个很棒的补丁了。虽然这是我失误,仔细阅读文档可能会更清晰明了,但我怀疑其他人也可能犯同样的错误。
实际上,在我们的测试套件中有两个用途,我相信其中之一也犯了这个错误。
在t1306.13中,没有跟踪的文件,所有在该测试中和以前测试中构建和使用的排除都必须与未跟踪的文件相关。
但是,由于他们寻找空结果,所以他们的错误命令也恰巧给出了空答案,这个错误就没有被发现。
-i
大部分时间将与-o
一起使用,这表明我们可以在缺少-o
或-c
的情况下使-i
暗示-o
,但这将是一种向后不兼容的破坏。
相反,让我们只标记没有-o
或-c
的-i
为错误,并更新两个相关的测试用例以指定它们的意图。
-c
,你将在 Git 2.32 (2021年第二季度)之后得到以下结果:fatal: ls-files -i must be used with either -o or -c
gitster
)修复,使用了提交 2c9f1bf和提交 1df046b。newren
) 提交。derrickstolee
) 提交。gitster
-- 合并于 提交 329d63e,2021 年 5 月 28 日)
dir
: 介绍readdir_skip_dot_and_dotdot()
辅助函数署名:Elijah Newren
这个解决方案增加了回车(我是WSL用户,所以这很重要),以及括号转义(有时对LaTeX用户很重要,例如*.synctex(busy)
)。
灵感来自Scott的解决方案:
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
。在前面添加是替换/^/
。你可以通过替换/$/
来进行追加。当然,由于我所知道的,在bash中不能创建原始字符串,因此需要转义一些字符。最后,这行可以被压缩,但是出于可读性的考虑,我选择保留它的扩展形式。
我看到有人在Scott的解决方案中质疑sed
是直截了当的。我认为这种方法是最基本和最朴素的方法,这很好,因为如果你需要这个变体的话,可以立即制作它。如果有什么问题,这是练习正则表达式的好借口。
如果您真的想要修剪您的历史记录中被 .gitignore 忽略的文件,首先将 .gitignore 保存在仓库外面,例如作为 /tmp/.gitignore,然后运行以下命令:
git filter-branch --force --index-filter \
"git ls-files -i -X /tmp/.gitignore -c | xargs -r git rm --cached --ignore-unmatch -rf" \
--prune-empty --tag-name-filter cat -- --all
注意:
git filter-branch --index-filter
运行在 .git
目录中,如果你想使用相对路径,需要先添加一个更多的 ../
。显然,你不能使用 ../.gitignore
,实际上的 .gitignore
文件,因为某些原因会导致 "fatal: cannot use ../.gitignore as an exclude file"(也许在执行 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
的内容),所以我无法在 git filter-branch
中使用 <(git show $GITIGNORE_HASH)
....gitignore
清理,那么请在最后一行中用它的名称替换 --all
。然而,--tag-name-filter cat
可能无法正常工作,也就是说,你可能无法直接正确地转移单个分支的标签。*.py~
文件,那么我的命令应该是 ==>
find . -name "*.py~" -exec rm -f {} \;
.gitignore
文件。 - S.S. Anne