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

Ale*_*nov 17 git git-filter-branch git-branch

假设我有这样的树:

... -- a -- b -- c -- d -- ...
             \
              e -- a -- k
Run Code Online (Sandbox Code Playgroud)

我希望它变得公正

... -- a -- b -- c -- d -- ...
Run Code Online (Sandbox Code Playgroud)

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

好.情况如下:我现在有一棵相当大的树(像这样)

                 s -- p -- r   
                /
a -- b -- c -- d -- e --- g -- w
           \               \
            t -- p -- l     y -- k
Run Code Online (Sandbox Code Playgroud)

但是在我的第一次提交中(比如前面的"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'
Run Code Online (Sandbox Code Playgroud)

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

Gre*_*con 37

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

假设您要从所有提交中删除"删除媒体文件"(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文档涵盖了该--prune-empty选项.

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

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

此时,由于另一个记录的行为,您将在存储库中看到重复.

原始引用(如果与重写的引用不同)将存储在命名空间中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伪ref并将其删除grep -v.对于其余部分,我们使用Perl去除refs/remotes/origin/前缀,并为每个运行表单的命令

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

  • +1好文章.正是我正在寻找的东西(即将工作中的大型回购拆分为多个回购,将它们用作子模块):) (2认同)