彻底从所有分支和标签中删除 git 代码库对象并将更改推送到远程

4
我们将客户迁移到网站上。我们的代码在一个单独的分支上,然后合并到主分支和发布分支中。自那时以来,主分支已经多次分支出其他功能。所有这些分支使得存储库比我在网上找到的示例稍微复杂一些。
现在我们意识到,客户的原始媒体 - 主要是图像和一个大的CSV文件 - 也被检入了Git。虽然只有12MB左右,但有几个原因需要删除它(最重要的是,客户的文件名具有非ASCII字符,在OSX上使用Vagrant虚拟机的共享文件夹时会遇到问题)。以下是存储库的大小统计:
$ du --max-depth 1 -h
12M  ./.git
13M  ./modules
2.0M ./themes
27M  .

尽管这些二进制文件显然现在已经出现在几个分支上,但据我所知,我应该能够只需执行以下操作即可删除这两个二进制文件,以及与之对应的存储库对象:
$ git filter-branch --tree-filter "git rm -rf --ignore-unmatch modules/custom/mymigration/data/photos/*" # Did this with and without "HEAD" argument
[snip lots of output]
$ git reflog expire --expire=now --all 
$ git gc --aggressive --prune=now

然而,我仍然有一个较大的 .git 子文件夹:

$ du --max-depth 1 -h
12M  ./.git
1.4M ./modules
2.0M ./themes
15M  .

最大的文件是.git/objects/pack/pack-....pack。当我验证这个文件的.idx文件时:
$ git verify-pack -v .git/objects/pack/pack-53c8077d0590dabcf5366589c3d6594768637f5e.idx | sort -k 3 -n | tail -n 5

我得到了一个很长的对象列表。如果我将其传递给rev-list,并在其中搜索我的迁移数据目录:

$ for i in `git verify-pack -v .git/objects/pack/pack-53c8077d0590dabcf5366589c3d6594768637f5e.idx | sort -k 3 -n | tail -n 5 | awk '{print $1}'`;    do 
    git rev-list --objects --all | \
      grep $i | \
      grep modules/custom/mymigration/data
  done
47846536601f0bc3a31093c88768b522a5500c96 modules/custom/mymigration/data/photos/Turkey.jpg
b920e36357d855352f4fdb31c17772d21c01304d modules/custom/mymigration/data/photos/Burger_Top.JPG

那么,正如您所看到的,照片仍然在包文件中。

  • 如果我将此存储库推送到(完全为空的)远程存储库中,然后在完全不同的位置克隆该远程存储库,则仍会有12MB的包文件。
  • 使用git clone file://path/to/old-repos new-repos在本地克隆此存储库也会产生相同的影响:更糟糕的是,所有我的原始分支都会消失(正如您可能预期的那样),因此我只有主分支。

有没有办法摆脱这些打包对象?它们的存在是否表明它们仍与某个git提交对象相关联?我已经尝试过repackprune-packed,但没有任何改变。

此外,如果我只是“摆脱它们”,如果我还没有正确地完成第一部分,是否会出现任何问题?如果删除了git提交仍然引用的文件对象会发生什么?


你在使用 filter branch 后修剪了仓库吗? - CharlesB
我已经运行了上面列出的命令。gc 命令有一个 --prune 选项。如果该选项意味着我已经修剪了仓库,那么我已经修剪了仓库;如果您指的是另一个命令,那么我可能没有修剪仓库,但知道您打算使用哪个命令会很好! :) - J-P
看起来在早期版本的git中,prune是一个单独的命令;我猜那些--prune命令意味着我确实在修剪。 - J-P
我已经更改了此帖子的标题,以便更清楚地描述基本问题,因此更容易搜索。 - J-P
2个回答

7
以下方法可将仓库大小缩减至约2.5MB的 .git 目录和总共 5.8MB,可以重复使用。它包含了 @jamessan 的建议。
此方法将从所有分支中删除对象,并将这些删除推送到远程仓库。据我所知,该远程仓库完全没有这些对象(因为仓库大小如此明显地下降)。请注意,HTML标签保留。
# Configure the repository to push all existing branches & tags
# when none are explicitly specified
git config --add remote.origin.push '+refs/tags/*:refs/tags/*'
git config --add remote.origin.push '+refs/heads/*:refs/heads/*'

# Make sure all local branches exist, so they get filtered
for remote_branch in `git branch --all | grep -v HEAD | sed -e 's/\*//'`; do local_branch=`echo $remote_branch | sed -e 's!remotes/origin/!!'`; git checkout $local_branch; done

# Prevent git <1.7.7.1 from complaining about dirty working directory
git update-index -q --ignore-submodules --refresh

# Do the filtering across --all branches and rewrite tags
# Note that this will necessarily remove signatures on tags
git filter-branch -f --tree-filter "git rm -rf --ignore-unmatch modules/custom/mymigration/data/photos/*" --tag-name-filter cat -- --all

# Remove the backed-up refs
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

# Clear out the reflog and garbage-collect
git reflog expire --expire=now --all
git gc --aggressive --prune=now

# Push all changes to origin - pushes tags and branches
git push origin

默认情况下,git push origin 不会推送标签。 - jamessan
第一行配置设置不就是解决这个问题的吗?这两行代码一起将无选项 push-origin 的默认行为更改为推送所有标签和分支引用。 - J-P
1
如果你想知道的话,第三个命令有一些别名,其中 g bagit branch --all,而 g cogit checkout。所以该命令可以是:for remote_branch in `git branch --all | grep -v HEAD | sed -e 's/\*//'`; do local_branch=`echo $remote_branch | sed -e 's!remotes/origin/!!'`; git checkout $local_branch; done - Nate Murray
@NateMurray 谢谢 - 我现在已经将它们合并到解决方案中了! - J-P

2

git-filter-branch手册底部描述了两种缩小代码库的方法。

简单的方法是重新克隆代码库。

git clone file:///path/to/repo

更复杂的方法类似于您所做的(reflog过期,gc),但您省略了第一步

删除由git-filter-branch备份的原始引用:git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d


克隆方法肯定不起作用:我仍然在.git文件夹中有12M的东西。我会将其添加到原始问题中,因为我现在记得自己这样做了。此外,“删除原始引用”的效果不是特别好,因为git-for-each只返回主分支。正如问题开头提到的那样,原始存储库有很多分支。 - J-P
1
回顾您的filter-branch命令,那是因为您没有告诉它在所有分支上操作。因此,您的存储库仍然引用了您尝试删除的文件。您可能想要运行 git filter-branch --tree-filter ... -- --all 以处理所有引用。 - jamessan
看起来我们正在并行解决这个问题!更重要的是,我认为我需要添加 --tag-name-filter cat,以添加任何需要指向新版本的标签:这听起来对吗? - J-P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接