【问题标题】:Removing git repository objects entirely from all branches and tags and pushing changes to remote从所有分支和标签中完全删除 git 存储库对象并将更改推送到远程
【发布时间】:2012-05-16 15:49:58
【问题描述】:

我们将客户迁移到一个网站。我们的代码在一个单独的分支上,然后合并到 master 和 release 中。从那以后,Master 也针对其他功能进行了多次分支。所有这些分支都使存储库比我在网络上找到的示例稍微复杂一些。

我们现在意识到客户的原始媒体——主要是图像和一个大的 CSV 文件——也被检入到 Git 中。虽然它只有 12MB 左右,但删除它有几个原因(尤其是客户端的文件名包含非 ASCII 字符,这与我们在 OSX 上的 Vagrant box 的共享文件夹玩得很糟糕。)这是存储库的大小细分:

$ 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,并为我的迁移数据目录使用 grep:

$ 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 在本地克隆此存储库也有相同的效果:更糟糕的是,我所有的原始分支都消失了(正如您可能期望的那样),所以我只有 master。

我能做些什么来摆脱那些包装好的物品吗?它们的持续存在是否表明它们仍然与某个地方的某个 git commit 对象相关联?我试过repack 和prune-packed 但没有任何改变。

此外,如果我只是“摆脱它们”,如果我没有正确完成第一步,是否有可能破坏?如果删除了 git commit 仍然引用的文件对象会怎样?

【问题讨论】:

  • 你在过滤器分支之后修剪了 repo 吗?
  • 我已经运行了上面列出的命令。 gc 有一个 --prune 选项。如果该选项意味着我已经修剪了回购,那么我已经修剪了回购;如果您指的是不同的命令,那么我可能没有修剪 repo,但最好知道您想要什么命令! :)
  • 看起来 prune 在早期版本的 git 中是一个单独的命令;我的猜测是,是的,那些 --prune 命令意味着我确实在修剪。
  • 注意我已经更改了这篇文章的标题,以便更清楚地描述潜在问题,因此更容易搜索。

标签: git indexing repository history


【解决方案1】:

以下工作可重复地将存储库减少到大约 2.5MB .git 和总共 5.8MB。它包括上面@jamessan 提出的建议。

这会从所有分支中删除对象并将这些删除推送到远程存储库。据我所知,该远程存储库完全没有这些对象(由于存储库大小急剧下降。)

# 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 默认不推送标签。
  • 配置设置的第一行不就是这样修复的吗?这两行一起改变了无选项 push-origin 的默认行为,以推送所有标签和分支 refs。
  • 如果您想知道,第三个命令有一些别名,其中g ba 是git branch --all,g co 是git 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
  • @NateMurray 谢谢 - 我现在已经将它们折叠到解决方案中!
【解决方案2】:

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

简单的方法是再次克隆存储库

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

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

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

【讨论】:

  • clone方法肯定不行:我的.git文件夹里还有12M。我将把它添加到原始问题中,因为我现在记得这样做。此外,“删除原始 refs”的效果不是特别好,因为 git-for-each 只返回 master 分支。如问题开头所述,原始存储库有很多分支。
  • 回头看看你的 filter-branch 命令,那是因为你没有告诉它对所有分支进行操作。因此,您的存储库仍然包含对您尝试删除的文件的引用。您可能希望运行 git filter-branch --tree-filter ... -- --all 以便处理所有 refs。
  • ... 看起来我们正在并行解决这个问题!更重要的是,我认为我需要添加--tag-name-filter cat,以添加任何需要指向新修订版的标签:听起来对吗?
猜你喜欢
  • 2011-10-15
  • 1970-01-01
  • 1970-01-01
  • 2015-11-05
  • 2011-01-09
  • 1970-01-01
  • 2013-11-01
  • 2011-07-08
  • 2013-02-21
相关资源
最近更新 更多