使用git子树合并,同时合并所有已合并子树的分支

8
我想使用一个流行的开源问题跟踪器(Redmine),它提供了git集成。不幸的是,跟踪器中的每个项目只能与一个git repo相关联。在跟踪器中创建多个项目并不是我的理想设置。
考虑到这一点,我尝试使用git子树合并(在这里这里解释)。我创建了一个“总体”repo,其中合并了我正在使用的众多其他repo。
不幸的是,给出的示例只拉取了每个子树的主分支。由于我在每个子树的多个分支中进行开发,因此我需要学习如何使这个总体repo反映每个子树的每个分支。
这可能吗?
额外加分:如果两个子树都有一个相同名称的分支怎么办?

第二个解释子树合并的链接已经失效 - 请尝试使用这里 - runexe
1个回答

7
对于不熟悉Redmine的我们,请您扩展说明以下问题:跟踪器需要哪种存取库的方式?它需要自己进行提交吗?还是只需要某些读取权限(例如验证提交哈希值并扫描提交日志中的关键字)?
如果您的跟踪器只需要读取访问权限,则可能根本不需要任何子树合并。在单个存储库中有多个初始提交(允许多个独立历史记录)是完全可以接受的。Git项目本身为某些“额外”(如manhtmltodo)执行此操作,这些内容与源代码的主要分支(maintmasternextpu)没有(提交)历史记录,但会与之一起发布。对于您的目的而言,可能足以为每个子存储库设置一个远程,并将其分支提示提取到聚合存储库中。也许自动的“远程跟踪分支”已经足够了,或者您需要采取额外步骤,基于远程跟踪分支创建(并更新)本地分支。
您描述的子树合并方案在源存储库的分支不相关或只有部分相关的一般情况下可能没有意义。但是,如果所有源存储库共享一组分支,其中每个分支在所有存储库中具有相同的特定目的,则可以将它们合并成一种超级存储库。
但有趣的问题不是“如果两个存储库具有相同名称的分支会怎样?”,而是“您如何处理缺少共享的‘全局’集合中的分支的存储库?” 如果所有子存储库都具有相同的一组分支,则只需像对master一样做,但对于每个分支执行一次。当存储库中缺少特定分支时就会出现问题。您可以替换其master,但那可能并不总是正确的答案。这取决于您首先为什么将存储库聚合在一起以及您希望在超级存储库的该分支子树中“看到”什么。
如果子存储库之间密切相关,则我确实对这种子树方法的合理性持怀疑态度。这种针对不相关存储库的方法感觉上可能“违背潮流”。虽然可能仍然可行,但我怀疑是否有任何工具可用,并且您需要花费一些时间来规划边缘情况。
如果您最终坚持使用子树合并,您可以查看第三方git subtree命令。它可能有助于保持您的多个存储库同步。

收集分支,无需合并

如果Redmine指定了 --mirror 克隆,那么它预期本地分支并且可能无法直接读取“远程跟踪分支”,因此您可能需要创建和更新一些本地分支。
  • Initial Setup

    mkdir $COLLECTION_REPO && cd $COLLECTION_REPO &&
    git init
    git remote add alpha <url/path-to-alpha-repo>
    git remote add bravo <url/path-to-bravo-repo>
    git remote add charlie <url/path-to-charlie-repo>
    for r in $(git remote); do
        git config --add remote.$r.fetch \
          "$(git config remote.$r.fetch | sed -e 's.heads.tags.;s.remotes.tags/all.')"
        git config remote.$r.tagopt --no-tags
    done
    
  • Periodic Update

    git remote update
    git for-each-ref --shell --format \
      'git branch --force --track -l all/%(refname:short) %(refname:short)' refs/remotes \
      | sh
    
  • Initial Setup

    mkdir $COLLECTION_REPO && cd $COLLECTION_REPO &&
    git init
    git remote add alpha <url/path-to-alpha-repo>
    git remote add bravo <url/path-to-bravo-repo>
    git remote add charlie <url/path-to-charlie-repo>
    for r in $(git remote); do
        git config remote.$r.fetch \
          "$(git config remote.$r.fetch | sed -e 's.remotes.heads/all.')"
        git config --add remote.$r.fetch \
          "$(git config remote.$r.fetch | sed -e 's.heads.tags.g')"
        git config remote.$r.tagopt --no-tags
    done
    
  • Periodic Update

    git remote update
    
两种方法都会收集refs/heads/all/<remote-name>/<branch-name-on-remote>下的分支,但第一种方法还有一个重复的引用集合在refs/remotes/<remote-name>/<branch-name-on-remote>下。第一种方法使用普通的fetch refspec,并使用git branch将“远程跟踪分支”(refs/remotes/…)复制到普通的本地分支(refs/heads/all/…)中。第二种方法使用自定义refspec将获取的refs直接存储到目标ref层次结构中。
由于更新是盲目地获取到这个组合仓库中的,因此没有人应该直接使用它:不要在其分支上直接进行提交,也不要从外部进行推送。如果有人在本地提交或推送到其中一个分支,那么当下次更新时,这些提交将被清除。
如果Redmine可以处理裸仓库,则建议使用裸仓库。使用git init --bare和以.git结尾的仓库名称。另外,git config core.logAllRefUpdates true可能是个好主意(因为在裸仓库中默认为false)。
除了命名空间中的all/前缀之外,这种方法与完全的--mirror克隆之间的另一个区别是,refs/headsrefs/tags之外的引用不会被收集。大多数其他常见的引用都被认为是存储在仓库中的“本地”(这就是为什么它们不会被正常克隆复制)。其他一些引用是“远程跟踪分支”(refs/remotes),一些是“二分记录”(refs/bisect),git filter-branch的“原始”ref备份(refs/original)等等。可能这些其他的东西对于Redmine来说都不重要。如果它们很重要,也可以使用其他的refspec进行包含。
创建额外的初始提交
要安排一个带有新初始提交的分支,请参见GitTips页面下的如何创建没有祖先的新分支。其中两个配方涉及到另一个仓库,在经过通常的init/add/commit步骤后,推送或获取一个分支(正是以上配方以自动化方式完成的)。

感谢您深思熟虑的回复。Redmine只需要对本地文件系统上的--mirror克隆进行只读访问。这些子存储库绝对不共享相同的众所周知的分支名称,这进一步使问题复杂化。远程跟踪分支/多个初始提交似乎很有前途,我会进行调查。 - anthony
跟进问题。我用什么命令创建多个初始提交? - anthony
+1 感谢提供的信息。我很遗憾只能点赞一次 :) 它对我在这个问题中的帮助很大 (http://stackoverflow.com/questions/10461142/converting-messy-svn-repository-to-git)。 - gingerlime

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接