如何在运行git filter-branch后删除旧的历史记录?

时间:2011-05-12 21:01:38

标签: git git-branch git-filter-branch

假设我有这样的树:

... -- a -- b -- c -- d -- ...
             \
              e -- a -- k

我希望它变得只是

... -- a -- b -- c -- d -- ...

我知道如何将分支名称附加到“e”。我知道我要做的事情会改变历史,这很糟糕。另外我想我需要使用像rebase或filter-branch这样的东西。但究竟是什么 - 我迷路了。

确定。情况如下:我现在有一个相当大的树(像这样)

                 s -- p -- r   
                /
a -- b -- c -- d -- e --- g -- w
           \               \
            t -- p -- l     y -- k

但是在我的第一次提交中(比如前面的“b”)我添加了二进制文件,这使整个回购非常繁重。所以我决定把它们带走。我用filter-branch做了。现在,从第二次提交开始,我有两个长的提交分支。

                 s -- p -- r   
                /
a -- b -- c -- d -- e --- g -- w
      \    \               \
       \    t -- p -- l     y -- k
        \
         \             s'-- p'-- r'  
          \           /
           b'-- c'-- d'-- e'--- g'-- w'
                 \               \
                  t'-- p'-- l'    y'-- k'

其中b'在没有二进制文件的情况下提交。所以我不能合并。我不希望这整个树在历史上重复。

5 个答案:

答案 0 :(得分:35)

导入具有多年历史记录的Subversion存储库后,我遇到了类似的问题,包括来自大量二进制资产的膨胀。在git: shrinking Subversion import中,我描述了将我的git repo从4.5 GiB修剪到大约100 MiB。

假设您要删除“Delete media files” (6fe87d)中删除的文件所有提交,您可以将我的博文中的方法改编为您的回购:

$ git filter-branch -d /dev/shm/git --index-filter \
  "git rm --cached -f --ignore-unmatch media/Optika.1.3.?.*; \
   git rm --cached -f --ignore-unmatch media/lens.svg; \
   git rm --cached -f --ignore-unmatch media/lens_simulation.swf; \
   git rm --cached -f --ignore-unmatch media/v.html" \
  --tag-name-filter cat --prune-empty -- --all

你的github repo没有任何标签,但我有一个标签名称过滤器,以防你有私人标签。

git filter-branch documentation涵盖了--prune-empty选项。

  

<强> --prune-empty
  某些类型的过滤器将生成空提交,使树保持不变。此开关允许git-filter-branch忽略此类提交...

使用此选项意味着您的重写历史记录不会包含“删除媒体文件”提交,因为它不再影响树。媒体文件永远不会在新历史记录中创建。

此时,由于另一个documented behavior,您会在存储库中看到重复。

  

原始引用(如果与重写的引用不同)将存储在命名空间refs/original/中。

如果您对新重写的历史记录感到满意,请删除备份副本。

$ git for-each-ref --format="%(refname)" refs/original/ | \
  xargs -n 1 git update-ref -d

Git对保护你的工作保持警惕,所以即使所有这些故意重写和删除reflog都会使旧的提交保持活力。用两个命令序列清除它们:

$ git reflog expire --verbose --expire=0 --all
$ git gc --prune=0

现在您的本地存储库已准备就绪,但您需要将更新推送到GitHub。你可以一次做一个。对于本地分公司,比如说大师,你要运行

$ git push -f origin master

假设您没有本地issue5分支。您的克隆仍然有一个名为origin / issue5的引用,它跟踪它在GitHub存储库中的位置。运行git filter-branch也会修改所有原点引用,因此您可以在没有分支的情况下更新GitHub。

$ git push -f origin origin/issue5:issue5

如果所有本地分支都与GitHub上的各自提交相匹配(即。,没有未提交的提交),那么您可以执行批量更新。

$ git for-each-ref --format="%(refname)" refs/remotes/origin/ | \
  grep -v 'HEAD$' | perl -pe 's,^refs/remotes/origin/,,' | \
  xargs -n 1 -I '{}' git push -f origin 'refs/remotes/origin/{}:{}'

第一阶段的输出是一个重新命名列表:

$ git for-each-ref --format="%(refname)" refs/remotes/origin/
refs/remotes/origin/HEAD
refs/remotes/origin/issue2
refs/remotes/origin/issue3
refs/remotes/origin/issue5
refs/remotes/origin/master
refs/remotes/origin/section_merge
refs/remotes/origin/side-media-icons
refs/remotes/origin/side-pane-splitter
refs/remotes/origin/side-popup
refs/remotes/origin/v2

我们不希望HEAD伪参考并使用grep -v将其删除。对于其余部分,我们使用Perl去除refs/remotes/origin/前缀,并为每个运行一个命令形式

$ git push -f origin refs/remotes/origin/BRANCH:BRANCH

答案 1 :(得分:1)

您可以再次使用git filter-branch ,但这次使用--parent-filter选项。有了这个,您可以通过将其父引用设置为空来取消链接提交。我认为你可以使用--commit-filter选项用于相同的目的。这会在你的仓库中留下很多不同的松散物体,所以你需要做git gc --prune = now。

以下是如何使用--parent-filter删除父项的示例 http://git.661346.n2.nabble.com/purging-unwanted-history-td1507638.html

答案 2 :(得分:0)

尝试:

  

git branch -d name

您可能需要使用此代码:

  

git branch -D name

答案 3 :(得分:0)

您可以使用git branch -D branch_name删除分支,并使用git push remote_name :branch_name删除远程分支。

提交将在您的存储库中保持未引用一段时间(请参阅git gc doc),但只会在您稍后意识到您犯了错误时使用磁盘空间。

由于您删除了远程分支,因此新git clone不应检索未引用的提交。

答案 4 :(得分:-2)

从您的示例中,您可以尝试git rebase b b'