git filter-branch导致了一个断开连接的历史记录:如何摆脱旧的提交?

时间:2013-07-26 16:16:10

标签: git tree git-filter-branch disconnected

方案如下:

我有一个很大的CVS存储库,我想将其转换为14个不同的git存储库。 该过程的cvs2git部分很好,并导致一个大的存储库repo.git。

对于14 git repo中的每一个,我克隆主repo并运行以下命令:

git filter-branch -d /tmp/rep --tag-name-filter cat --prune-empty --subdirectory-filter "sub/directory" -- --all

但是,在此命令之前,我必须为某些git存储库执行另一个git filter-branch命令,因为我必须重写提交以将文件从目录移动到另一个目录。 --tree-filter是我使用的选项。以下是执行命令行的示例:

script_tree_filter="if test -f rep/to/my/file && test -d another/rep ; then echo Moving my file ; mv rep/to/my/file another/rep; fi"
git filter-branch -d /tmp/rep --tag-name-filter cat --prune-empty --tree-filter '$script_tree_filter' -- --all

在流程结束时(14500提交:大约需要1小时!)我清理参考并使用git gc

git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git reflog expire --expire=now --all
git gc --prune=now

最后,我获得了一个大小为1.2Go的存储库(显然仍然太大了),通过查看提交,我可以看到很多旧的仍然存在。它们涉及在--subdirectory-filter命令之后不再存在的文件和目录。

在提交历史中,不需要的提交与好的提交之间存在不连续性,如gitk --all所示:

discontinuity seen in gitk

我很确定那些提交仍然存在,因为它们上面有一些标记。如果是这种情况,是否可以在不删除良好提交的标签的情况下删除这些标签?

如果标签不是原因,任何想法?

有关更多信息,refs目录(在子目录过滤器获取的git存储库中)的内容为空:

$ ls -R refs/
refs/:
heads  original  tags

refs/heads:

refs/original:
refs

refs/original/refs:
heads  tags

refs/original/refs/heads:

refs/original/refs/tags:

refs/tags:

我发现分支和标签列在git存储库的文件packed-refs中:

d0c675d8f198ce08bb68f368b6ca83b5fea70a2b refs/tags/v03-rev-04
95c3f91a4e92e9bd11573ff4bb8ed4b61448d8f7 refs/tags/v03-rev-05

文件中列出了817个标签和219个分支。

2 个答案:

答案 0 :(得分:5)

我设法通过改变我使用cvs2git的方式解决了我的问题:我没有转换整个CVS基础然后使用subdirectory-filter命令,而是转换了我想要的每个子模块。就我而言,这导致了18个不同的cvs2git命令:

<强>之前

cvs2git --blobfile=blob --dump=dump /path/to/cvs/base
# Module 1
git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter "path/to/module1" -- --all
# Module 2
git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter "path/to/module2" -- --all

立即

# Module 1
cvs2git --blobfile=blob_module1 --dump=dump_module1 /path/to/cvs/base/path/to/module1
# Module 2
cvs2git --blobfile=blob_module2 --dump=dump_module2 /path/to/cvs/base/path/to/module2

每个存储库现在都有完美的历史记录。

为什么以前的方法不起作用?我的猜测是cvs2git与所有子模块相混淆(其中一些子模块的历史记录中有一些目录名称已更改)。

@Michael @CharlesB感谢您抽出宝贵时间回答并帮助我。

答案 1 :(得分:2)

我打赌你受到了这个打击:

  
      
  • CVS和git分支/标记模型之间的差异:CVS允许从多个源分支的源修订的任意组合创建分支或标记。它甚至允许将从未同期的文件修订添加到单个分支/标记中。另一方面,Git只允许在历史中某个时刻存在的完整源代码树作为一个单元进行分支或标记。此外,git修订版的祖先会对该修订版的内容产生影响。这种差异意味着从根本上不可能100%忠实地在git存储库中表示任意CVS历史记录。 cvs2git使用以下解决方法:

         
        
    • cvs2git尝试从单个源创建分支,但如果它无法弄清楚如何,则使用来自多个源分支的“合并”创建分支。在病态情况下,分支的合并源的数量可以是任意大的。生成的历史记录意味着无论何时将任何文件添加到分支,整个源分支都会合并到目标分支中,这显然是不正确的。 (另一种方法是,省略合并,将丢弃某些内容从一个分支移动到另一个分支的信息。)

    •   
    • 如果cvs2git无法确定可以从单个修订版创建CVS标记,那么它会创建一个名为TAG.FIXUP的标记修复分支,然后标记此分支。 (这是git仅允许标记现有修订的事实的必要解决方法。)TAG.FIXUP分支创建为包含标记中包含的文件修订的所有分支之间的合并,其涉及描述的相同权衡以上为分支机构。 TAG.FIXUP分支在转换结束时被清除,但(由于git快速导入文件格式的技术限制)未被删除。在某些情况下,可以从单个修订版创建标记,但cvs2git没有意识到它并创建了多余的标记修复分支。通过在生成的git存储库中运行contrib / git-move-refs.py脚本,可以在转换后删除多余的标记修复分支。

    •   
  •   
  • 没有检查CVS分支和标记名称是合法的git名称。可能还应该检查其他git约束。 see cvs2git

  •   

您是在转换后显示新目录的refs目录还是大型仓库的refs目录?在过滤和拆分大型仓库之前,您可以删除单个大型导出仓库中的标签。

您只需删除目录中的文件即可删除大型仓库中的标签 - 它只是对SHA的引用。