从git/GitHub的历史记录中删除文件夹及其内容

Kar*_*tik 284 git github rebase git-rebase

我正在使用我的GitHub帐户上的存储库,这是我偶然发现的一个问题.

  • Node.js项目包含一个安装了几个npm软件包的文件夹
  • 包裹在node_modules文件夹中
  • 将该文件夹添加到git repository并将代码推送到github(当时没有想到npm部分)
  • 意识到你并不真的需要该文件夹成为代码的一部分
  • 删除了那个文件夹,推了它

在那个例子中,总git repo的大小约为6MB,其中实际代码(除了该文件夹之外的所有代码)只有大约300 KB.

现在我正在寻找的是从git的历史中删除该包文件夹的细节的一种方法,所以如果有人克隆它,他们不必下载6mb的历史记录,他们将获得唯一的实际文件截至上次提交时将为300KB.

我查找了可能的解决方案并尝试了这两种方法

Gist似乎在运行脚本之后起作用,它表明它摆脱了该文件夹,之后它显示了50个不同的提交被修改.但它没有让我推动那些代码.当我试图推它时,它说,Branch up to date但显示50个提交被修改了一个git status.其他两种方法也没有帮助.

现在即使它显示它摆脱了该文件夹的历史记录,当我在我的localhost上检查该repo的大小时,它仍然是大约6MB.(我也删除了refs/original文件夹,但没有看到repo大小的变化).

我要澄清的是,如果有一种方法可以摆脱提交历史(这是我认为发生的唯一事情),而且那些文件git保持假设一个人想要回滚.

让我们说一个解决方案是为此而呈现并应用于我的localhost但不能复制到该GitHub仓库,是否有可能克隆该repo,回滚到第一个提交执行技巧并推送它(或者这是否意味着git将仍然有所有这些提交的历史? - 又名.6MB).

我的最终目标是基本上找到从git中删除文件夹内容的最佳方法,这样用户就不必下载6MB的东西,仍然可能有其他提交从未触及过模块文件夹(那很漂亮)他们中的所有人都在git的历史中.

我怎样才能做到这一点?

Moh*_*sen 500

如果你在这里复制粘贴代码:

这是一个node_modules从历史中删除的例子

git filter-branch --tree-filter "rm -rf node_modules" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force
Run Code Online (Sandbox Code Playgroud)

  • 在运行命令后我还要运行`git gc`以释放被删除引用所使用的所有空间. (18认同)
  • 值得注意的是,如果你需要推动这个上游,你可能需要使用`git push origin master --force强制进行非快进更新. (16认同)
  • 这些命令都不适用于Windows.或者至少不是Windows 10,请发布"剪切和粘贴"工作的操作系统 (4认同)
  • 请注意:我使用`git count-objects -v`来检查文件是否实际被删除但是存储库的大小保持不变,直到我再次克隆存储库.Git保留了我认为的所有原始文件的副本. (3认同)
  • 对于非古老的git,这应该是`--force-with-lease`,而不是`--force`. (3认同)
  • 对于Windows 10用户,这适用于Bash for Windows(我使用的是Ubuntu) (3认同)
  • 我尝试使用Windows shell和git bash,但没有用.第一个命令传递,第二个命令失败! (3认同)
  • 卡在git for-each-ref --format ="%(refname)"refs/original/| xargs -n 1 git update-ref -d echo node_modules/>> .gitignore (2认同)
  • `git for-each-ref --format ="%(refname)"refs/original/| xargs -n 1 git update-ref -d`命令在ubuntu中不起作用. (2认同)

Lee*_*ton 208

我发现--tree-filter其他答案中使用的选项可能非常慢,特别是在具有大量提交的大型存储库中.

这是我用来从git历史中使用--index-filter选项完全删除目录的方法,该选项运行得更快:

# Make a fresh clone of YOUR_REPO
git clone YOUR_REPO
cd YOUR_REPO

# Create tracking branches of all branches
for remote in `git branch -r | grep -v /HEAD`; do git checkout --track $remote ; done

# Remove DIRECTORY_NAME from all commits, then remove the refs to the old commits
# (repeat these two commands for as many directories that you want to remove)
git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch DIRECTORY_NAME/' --prune-empty --tag-name-filter cat -- --all
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

# Ensure all old refs are fully removed
rm -Rf .git/logs .git/refs/original

# Perform a garbage collection to remove commits with no refs
git gc --prune=all --aggressive

# Force push all branches to overwrite their history
# (use with caution!)
git push origin --all --force
git push origin --tags --force
Run Code Online (Sandbox Code Playgroud)

您可以在gc使用之前和之后检查存储库的大小:

git count-objects -vH
Run Code Online (Sandbox Code Playgroud)

  • 为什么这不是公认的答案?它是如此彻底. (22认同)
  • 将`--quiet`传递给上面的`git rm`至少加速了我的重写4倍. (10认同)
  • @knocte:来自docs(https://git-scm.com/docs/git-filter-branch)."--index-filter:...类似于树过滤器,但不检查树,这使得它更快" (7认同)
  • 你能解释为什么这会更快吗? (2认同)
  • 如果在Windows中执行此操作,则需要双引号而不是单引号。 (2认同)
  • 如果您遇到 xargs 问题,则需要将其添加到您的路径(program files\git\usr\bin)。 (2认同)

And*_*jos 74

似乎对此的最新答案是filter-branch直接使用(至少 git 本身不再推荐它),并将该工作推迟到外部工具。目前特别推荐git-filter-repo。该工具的作者提供了为什么filter-branch直接使用会导致问题的论据

上面dir要从历史记录中删除的大多数多行脚本都可以重写为:

git filter-repo --path dir --invert-paths
Run Code Online (Sandbox Code Playgroud)

显然,该工具比这更强大。您可以按作者、电子邮件、参考名称等应用过滤器(此处为完整联机帮助页)。此外,它速度很快。安装很容易——它以多种格式分发

  • 好工具!在 Ubuntu 20.04 上运行良好,您只需“pip3 install git-filter-repo”,因为它仅包含 stdlib,并且不安装任何依赖项。在 Ubuntu 18 上,它与发行版的 git 版本不兼容“错误:需要一个其 diff-tree 命令具有 --combined-all-paths 选项的 git 版本”,但在“docker run -ti ubuntu”上运行它很容易:20.04` (6认同)
  • 谢谢你,这很快,几秒钟内就完成了!关于使用的一些注意事项:1)您可能需要安装更新版本的 git。如果您使用的是 ubuntu,可能需要设置一个新的 apt 存储库,因为即 Xenial 存储库仍在 git 2.7.4 上,该版本太旧了。2)这也会在本地删除该文件夹。如果需要的话请备份。3) 您需要重新添加远程 url 并执行强制推送(一如既往,小心!)。4) 您可以使用`pip3`轻松安装该工具(如上所述)。5) 如果您不想克隆新的存储库,则可能需要使用“--force”运行。似乎对我来说一切顺利。 (4认同)
  • `git: 'filter-repo' 不是 git 命令。请参阅“git --help”。 (3认同)
  • 在 OS X 上,具有 Homebrew [链接](https://brew.sh) 支持。`brew 安装 git-filter-repo` (3认同)
  • 该示例应为“git-filter-repo.py”,而不是“git filter-repo”。它不是本机 Git 命令。 (2认同)
  • 我必须推动改变吗?运行此命令后,本地存储库不再有任何遥控器。添加遥控器后推送会出现错误。是否需要“git push origin main --force”? (2认同)

par*_*ant 41

除了上面流行的答案,我想为Windows系统添加一些注释.命令

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
Run Code Online (Sandbox Code Playgroud)
  • 完美无缺,无需任何修改!因此,你不能使用Remove-Item,del或其他任何东西代替rm -rf.

  • 如果你需要指定路径的文件或目录,使用斜线./path/to/node_modules

  • 我找到了解决方案.对于rm命令使用双反转逗号,如下所示:"rm -rf node.modules". (4认同)
  • 在Linux上,它也是最完美,最简单的命令。 (2认同)

Kim*_*m T 21

我找到的最好和最准确的方法是下载bfg.jar文件:https://rtyley.github.io/bfg-repo-cleaner/

然后运行命令:

git clone --bare https://project/repository project-repository
cd project-repository
java -jar bfg.jar --delete-folders DIRECTORY_NAME  # i.e. 'node_modules' in other examples
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --mirror https://project/new-repository
Run Code Online (Sandbox Code Playgroud)

如果要删除文件,请使用delete-files选项:

java -jar bfg.jar --delete-files *.pyc
Run Code Online (Sandbox Code Playgroud)

  • 但是,当存在多个与要删除的特定文件夹同名的文件夹时,使用 BFG 可能会遇到麻烦,即 BFG 无法接受“--delete-folders”的路径名。 (2认同)

jgb*_*rah 6

完成复制和粘贴配方,只需在测试后添加注释中的命令(用于复制粘贴解决方案):

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force
Run Code Online (Sandbox Code Playgroud)

在此之后,您可以从.gitignore中删除"node_modules /"行


kco*_*ode 5

对于Windows用户,如果已经存在另一个备份,请注意使用"而不是' 也添加-f来强制执行该命令。

git filter-branch -f --tree-filter "rm -rf FOLDERNAME" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo FOLDERNAME/ >> .gitignore
git add .gitignore
git commit -m "Removing FOLDERNAME from git history"
git gc
git push origin master --force
Run Code Online (Sandbox Code Playgroud)