【问题标题】:Intense Restructure of large Git repo into multiple new repos将大型 Git 存储库密集重组为多个新存储库
【发布时间】:2023-03-24 00:30:01
【问题描述】:

我找到了几个同时使用过滤器分支和子树的简单示例,但它们总是只是移动 1 个目录。我想采取以下回购:

/
  Project1.sln
  Project2.sln
  Source/
    CommonLib.Data/
    CommonLib.Web/
    Project1.Data/
    Project1.Web/
    Project1.Other/
    Project2.Data/
    Project2.Web/

然后将内容移到它们自己的存储库中,结构如下:

# CommonRepo
/
  CommonLib.Data/
  CommonLib.Web/

# Project1Repo
/
  Project1.sln
  Project1.Data/
  Project1.Web/
  Project1.Other/

# Project2Repo
/
  Project2.sln
  Project2.Data/
  Project2.Web/

同时维护整个历史记录。更复杂的是,每个项目对应的原始 repo 有 1 个或多个分支,因此其他项目引用的 CommonLib 版本可能略有不同。

我想使用 git subtree add 在每个新存储库中以正确的标签/修订版添加对 CommonLib 的引用,但首先我需要一种方法将多个目录一次拆分到它们自己的位置.

git subtree split -P 似乎只需要 1 个目录,而且我也无法让 filter-branch 获取倍数。我在一个 windows 盒子上,所以没有设置所有的脚本细节来让这更容易。

有什么建议吗?

【问题讨论】:

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


    【解决方案1】:

    最后,我建议您将公共库包含在您的项目中,特别是由于您谈到的分歧,所以您理想的结构应该是:

    # CommonRepo
    /
      CommonLib.Data/
      CommonLib.Web/
    
    # Project1Repo
    /
      Project1.sln
      Project1.Data/
      Project1.Web/
      Project1.Other/
      CommonLib/         # I recommend that you do whatever restructuring needed to support this in a sub-directory
        CommonLib.Data/
        CommonLib.Web/
    
    # Project2Repo
    /
      Project2.sln
      Project2.Data/
      Project2.Web/
      CommonLib/         # I recommend that you do whatever restructuring needed to support this in a sub-directory
        CommonLib.Data/
        CommonLib.Web/
    

    现在处理拆分:

    当您拆分时,只要您不使用不同的注释或提交 id 将兼容的东西,并且应该与合并很好地配合。因此,您可以从自己提取 CommonLib 开始。

    1. 我建议您在开始之前克隆整个 depo,以确保您不会丢失任何东西。

      git clone <big-repo> <big-repo-clone>
      
    2. 准备旧的仓库

      pushd <big-repo-clone>
      # split for the common lib
      git checkout master  # assuming you want your common lib at master
      git subtree split --prefix=Source --branch=temp-commonLib
      
      # split the projects from their respective branches
      git checkout <branch-for-project1>
      git subtree split --prefix=Source --branch=temp-project1
      
      # split the projects from their respective branches
      git checkout <branch-for-project2>
      git subtree split --prefix=Source --branch=temp-project2
      
    3. 现在我们需要清除那些我们不想要的项目部分。由于它们混合在一起,因此您不能真正使用子树,但您可以 filter-branch 重写历史记录,而无需其他部分。

      # strip unrelated parts from the CommonLib
      git checkout temp-commonLib
      git filter-branch --tag-name-filter cat --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch Project1* Project2*' HEAD
      
      # strip unrelated parts from the Project1
      git checkout temp-project1
      git filter-branch --tag-name-filter cat --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch CommonLib* Project2*' HEAD
      
      # strip unrelated parts from the Project2
      git checkout temp-project2
      git filter-branch --tag-name-filter cat --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch CommonLib* Project1*' HEAD
      

      prune empty 将删除变为空的提交,因为它们只包含您删除的文件夹中的更改。

      注意:所有这些更改都在 /source 级别,因此它可以成为每个项目的新根。您可以稍后重新添加您的解决方案。或者您可以将这种修剪技术与克隆一起使用而不是子树,当您完成所有操作后,您可以将所有内容从“/Source”移动到“/”

      现在您将在refs/original/refs/heads/&lt;branch-name&gt; 中拥有额外的分支和备份。如果在此过程中您遇到了 filter-branch 的致命错误,您可以重新创建分支并重新开始,或者如果您确信它没有执行任何操作,您可以使用以下命令删除此备份:git update-ref -d refs/original/refs/heads/&lt;branch-name&gt;

    4. 现在只需创建新的存储库来存储从这些分支创建的项目

      popd # to get out of <big-repo-clone>
      
      mkdir <new-repo>
      pushd <new-repo>
      
      git init
      git pull <big-repo-clone> <name-of-branch> # like temp-project1
      popd # to get out of the <new-repo>
      
    5. 最后一件事,让我们将 CommonRepo 拉入项目中。

      pushd <new-project-repo>
      git subtree add --prefix=CommonLib <new-commonlib-repo>
      

    然后你只需要引入 .sln 文件(我将把最后一步留给你)。

    【讨论】:

    • 我认为你不需要一次指定多个目录来实现你的目标
    • 在您的示例和我的帖子的编辑中,我认为您已经重新组织了我的层次结构。每个项目的 web 和非 web 部分都是对等的,web 目录在需要移动的其他目录下不存在。
    • 抱歉。我无法真正理解您编写的目录结构。这是你的意思吗(见我帖子中的编辑)
    • 不,与给定项目关联的两个目录没有公共路径。我会编辑你的,以更准确地反映它,并在我简化后显示额外的复杂性。
    • 我已经根据更新后的初始结构更新了我的答案,并对其进行了测试。
    【解决方案2】:

    我的程序 git_filter 可以为你做拆分。我写它是因为所有其他解决方案在我们的大型存储库上都非常慢。在这里:

    https://github.com/slobobaby/git_filter

    它为原始存储库的每个提取创建多个分支。目前我在这里有一个测试分支:

    https://github.com/slobobaby/git_filter/tree/subdir

    这将创建一个新分支,其中包含重命名为新存储库根目录的原始存储库的子目录。

    与基于 git-core 的解决方案相比,运行需要数小时或数天。

    包含一个脚本,然后将这些新分支推送到新的干净存储库。

    【讨论】:

    • 糟糕,我没有看到你在 Windows 上。你总是可以启动一个带有 linux 的虚拟机......或者将我的代码移植到 windows。
    • 我昨天在搜索时找到了你的项目,但我不知道如何配置它来做我想做的事。如果原始仓库有多个分支,这些分支是否会被工具带到新的仓库中?
    • 没有。作为流程的第二阶段,每个分支都被推送为一个新的单独存储库。重用原始对象而不是创建新对象确实有助于加快进程速度。 push_clean_repos 脚本创建干净的存储库。
    • 如果我阅读了自己的代码,我可能会看到 subdir branh 与您想要的相反,并将根目录移至子目录。 github.com/slobobaby/git_filter/tree/flatten 的这个新分支可以满足您的需求。我什至添加了重写您上面描述的示例存储库的配置。只需在当前目录下的 test 中签出并生成并运行 ./git_filter test.cfg 即可:)
    • 在回答关于多个分支的问题时,答案是默认情况下它只需要一个分支并对其进行过滤。您需要多次运行它来过滤多个分支。我可能会将其更改为在所有提交上执行,而不仅仅是某个分支的。
    【解决方案3】:

    对我来说,与其试图弄清楚如何拆分回购的许多部分,我更喜欢克隆原始版本,然后通过切掉我不想要的部分来削减它。然后在原始(或另一个克隆)中,我去掉了我拆分的部分。我发现它是一种更简单的迭代方法,对正在发生的事情有更大的可见性。

    在此处查看我最近对此主题的回答:https://stackoverflow.com/a/22210682/955926

    【讨论】:

      猜你喜欢
      • 2011-04-24
      • 1970-01-01
      • 2012-03-31
      • 2016-03-12
      • 2023-04-06
      • 2011-05-28
      • 1970-01-01
      • 1970-01-01
      • 2019-05-06
      相关资源
      最近更新 更多