使用git子树合并,同时也合并所有合并子树的所有分支 [英] Using git subtree merging, while also merging in all branches of all merged subtrees

查看:136
本文介绍了使用git子树合并,同时也合并所有合并子树的所有分支的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用提供git集成的流行开源问题跟踪器(Redmine)。不幸的是,跟踪器中的每个项目只能与一个git仓库相关联。在追踪器中创建多个项目并不是我理想的设置。



考虑到这一点,我试图使用git子树合并(解释 here here )。我已经创建了一个伞回购,它已经并入了我正在使用的许多其他回购协议中。



不幸的是,每个子树的主分支。由于我在每个子树的多个分支中进行开发,因此我需要学习如何让这个伞回购反映每个子树的每个分支。



这可能吗?



额外信贷:如果2个子树都有一个同名的分支?

对于我们不熟悉Redmine的人员,请将您的描述扩展为包含以下问题的答案:跟踪器需要对存储库进行什么样的访问?它是否需要进行自己的提交?或者,它是否只需要某些类型的读取权限(也许可以验证提交哈希和扫描提交日志的关键字)?



如果您的跟踪器只需要读取访问权限,根本不需要任何子树合并。在一个存储库中有多个初始提交(允许多个独立的历史记录)是完全可以接受的。 Git项目本身是为一些'额外'( man html todo )共享no(commit)历史记录的,但是与源代码主要分支一起发布( master next pu )。出于您的目的,为每个子存储库设置一个远程服务器并将其分支提示提取到您的聚合存储库中可能已足够。也许自动'远程追踪分支'就足够了,或者你需要额外的步骤来创建(和更新)基于远程追踪分支的本地分支。



您描述的子树合并方案在源存储库中的分支不相关或仅半相关的一般情况下可能没有意义。但是,如果所有源存储库共享一组分支,其中每个分支在所有存储库中具有相同的特定目的,则可以将它们有意义地合并为一种超级存储库。



但有趣的问题不是如果两个存储库具有相同名称的分支?,而是如何处理存储库缺少来自共享'全局'集的分支的情况?。
$ b

如果所有子存储库都有相同的分支集合,那么您只需按照 master 完成的操作,但是一次每个分支。当存储库中缺少特定的分支时会出现问题。您可以替换它的 master ,但这可能并不总是正确的答案。这取决于你为什么首先将这些存储库聚合在一起,以及你希望在超级存储库中该分支的子树中看到什么。



如果子库不是密切相关的,那么我真的怀疑这个子树方法的合理性。对于不相关的储存库来说,这种方法会让人觉得它会违背粮食。这可能还是有可能的,但我怀疑是否有任何工具可以帮助你,并且你需要花一些时间计划出错的情况。



如果你最终坚持通过子树合并,您可以查看第三方 git subtree 命令。它可能有助于保持您的无数存储库同步。






收集分支,不合并



如果Redmine指定 - mirror clone,则意味着它期望本地分支并且可能无法直接读取远程跟踪分支所以您可能需要创建和更新一些本地分行。



从'远程追踪分行'更新的本地分行


  • 初始设置

      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 $ in $(git remote);做
    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


  • 定期更新

      git远程更新
    git for-each-ref - -shell --format \
    'git branch --force --track -l all /%(refname:short)%(refname:short)'refs / remotes \
    | sh




直接接收分支提示的本地分支


  • 初始设置

      mkdir $ COLLECTION_REPO&& amp ; 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 $ in $(git remote);做
    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
    完成


  • 定期更新

      git远程更新




这两种方法最终在 refs / heads / all /< remote-name> /< branch-name-on下收集分支-remote> ,但是第一个也在 refs / remotes /< remote-name> /< <远程分支名称>< / code>。第一个使用普通的fetch refspec,并使用 git branch 来复制'远程追踪分支'( refs / remotes / ... )到正常的本地分支( refs / heads / all / ... )。第二个使用自定义refspec将提取的ref直接存储到目标ref层次结构中。

由于更新被盲目地提取到这个组合存储库中,所以没有人应该曾尝试直接使用它:不直接在其分支上提交,也不从外部推送。如果有人在本地进行提交或将其推送到其中一个分支,那么在下一次更新完成时,这些提交将被清除。

如果Redmine可以处理裸仓库,我会推荐使用一个。使用 git init --bare 和一个以.git结尾的回购商标名称。 git config core.logAllRefUpdates true 可能是一个好主意(因为它在裸仓库中默认为false)。



<除了名称空间中的 all / 前缀之外,这种方法与完整的 - mirror clone的另一个区别是在 refs / heads refs / tags 之外的引用将不会被收集。大多数其他常见参考资料被认为是对存储库的本地(这就是为什么它们不能被正常克隆复制的原因)。一些其他的裁判是'远程追踪分支'( refs / remotes ),一些'bisect'记录保存( refs / bisect ), git filter-branch 'original'ref备份( refs / original )等等。 Redmine可能没有其他的东西是重要的。如果他们是,他们也可以包含额外的refspecs。



创建额外的初始提交



安排具有新初始提交的分支,请参阅 GitTips页面。 http://git.wiki.kernel.org/index.php/GitTips#How_to_create_a_new_branch_that_has_no_ancestorrel =noreferrer>如何创建一个没有祖先的新分支。其中两个配方涉及另一个存储库,在完成通常的init / add / commit步骤之后,您可以从其中推入或取出分支(这正是上述配方以自动方式执行的操作)。


I'd like to use a popular, open source issue tracker (Redmine) that offers git integration. Unfortunately, each project in the tracker can only be associated with one git repo. Creating multiple projects in the tracker is not my ideal setup.

With that in mind, I've attempted to use git subtree merging (explained here, and here). I've created an "umbrella" repo which has merged in each of the numerous other repos that I'm working with.

Unfortunately, the examples given only pull in the master branch of each subtree. Since I have development going on in multiple branches of each subtree, I need to learn how to have this umbrella repo reflect each branch of each subtree.

Is this possible?

Extra Credit: What if 2 subtrees each have a branch with the same name?

解决方案

For those of us not familiar with Redmine, please extend your description to include answers to the following questions: What kind of access into the repository does the tracker need? Will it need to make its own commits? Or, does it just need certain kinds of read access (perhaps to validate commit hashes and scan commit logs for keywords)?

If your tracker only needs read access, you may not need any subtree merging at all. It is perfectly acceptable to have multiple initial commits (allowing multiple, independent histories) in a single repository. The Git project itself does this for some ‘extras’ (man, html, todo) that share no (commit) history with, but are published alongside the main set of branches for the source code (maint, master, next, pu). For your purpose, it may be enough to setup a remote for each sub-repository and fetch their branch tips into your aggregating repository. Maybe the automatic ‘remote tracking branches’ would be enough, or maybe you need to take the extra step to create (and update) local branches based on the remote tracking branches.

The subtree merging scheme you describe is probably not meaningful in the general situation where the branches in the source repositories are unrelated or only semi-related. But, if all the source repositories share a set of branches where each branch has a given purpose that is the same across all the repositories, you could probably meaningfully merge them into a kind of super-repository.

But the interesting question is not "what if two repositories have branches with the same name?", but "how do you handle the case where a repository is missing a branch from the shared, ‘global’ set?".

If all the sub-repositories have the same set of branches, you just do what you did with master, but once for each branch. The problem comes when a particular branch is missing from a repository. You could substitute its master, but that may not always be the right answer. It depends on why you are aggregating the repositories together in the first place and what you expect to ‘see’ in that subtree of that branch in the super-repository.

If the sub-repositories are not closely related, then I really have my doubts about the reasonableness of this subtree approach. Such an approach for unrelated repositories feels like it would be ‘going against the grain’. It is probably still possible, but I doubt there is any tool to help, and you will need to spend some time planning out the corner cases.

If you end up sticking with subtree merges, you might look at the third-party git subtree command. It might help in keeping your myriad repositories synchronized.


Collecting Branches, Without Merging

If Redmine specifies --mirror clone, the implication is that it expects local branches and may not be able to directly read the ‘remote tracking braches’, so you will probably need to create and update some local branches.

Local Branches Updated From ‘Remote Tracking Branches’
  • 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
    

Local Branches That Directly Receive Fetched Branch Tips
  • 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
    

Both methods end up collecting branches under refs/heads/all/<remote-name>/<branch-name-on-remote>, but the first also has a duplicate set of refs under refs/remotes/<remote-name>/<branch-name-on-remote>. The first uses a normal fetch refspec and uses git branch to duplicate the ‘remote tracking branches’ (refs/remotes/…) into normal, local branches (refs/heads/all/…). The second one uses a custom refspec to store the fetched refs directly into the destination ref hierarchy.

Because of the way updates are blindly fetched into this combined repository, no one should ever try to directly use it: no commits made directly on its branches, no pushes from outside. If someone were to make commits locally or to push onto one of the branches those commits would be wiped out when the next update is done.

If Redmine can handle a bare repository, I would recommend using one. Use git init --bare and a repo name that ends with .git. Also git config core.logAllRefUpdates true might be a good idea (since this defaults to false in a bare repository).

Besides the all/ prefix in the namespaces, another difference between this approach and a full --mirror clone is that refs outside refs/heads and refs/tags will not be collected. Most of the other common refs are considered ‘local’ to a repository (which is why they are not copied by a normal clone). Some of the other refs are ‘remote tracking branches’ (refs/remotes), some ‘bisect’ record keeping (refs/bisect), git filter-branch ‘original’ ref backups (refs/original), et cetera. Probably none of these other things are important for Redmine. If they are, they can also be included with additional refspecs.

Creating Extra Initial Commits

To arrange for a branch with an new initial commit, see GitTips page under How to create a new branch that has no ancestor. Two of the recipes involve another repository from which you push or fetch a branch after going through the usual init/add/commit step (exactly what the above recipes do in an automated way).

这篇关于使用git子树合并,同时也合并所有合并子树的所有分支的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆