17

我有一个项目需要分成两个存储库:一组通用模型,以及基于这些模型的模拟,以及附加代码。最终可能会有多个模拟使用同一组模型,因此将它们放在单独的存储库中是一个明确的要求。显而易见的解决方案是将通用模型作为模拟的子模块。

不幸的是,这两个存储库将高度耦合。人们会经常在他们的通用模型中添加一些东西,然后立即在模拟中使用它。我想这会给模拟仓库的集成过程带来很多麻烦。为了在仿真中合并来自许多开发人员的更改,集成商必须在通用模型子模块中进行并行合并。另一方面,它也使得使用子模块变得必不可少——仿真确实需要知道它应该使用哪个版本的通用模型。

该项目由相当多的人参与。大多数开发人员对 git 的了解非常粗略:他们经常添加文件、提交和从源中提取,并希望有一个开发和稳定的分支。集成商自然学到了很多东西,但是任何涉及子模块的东西对他来说肯定是新的。额外的好处:我即将休假一个月,所以我无法扑灭任何火灾。结果是,有很多动力让工作流程真的很难搞砸,并尽量减少与人们以前工作流程的差异。

所以,我的问题是:我会后悔推荐我们为此使用子模块吗?(有更好的主意吗?)我可以期待人们犯什么样的错误,所以我可以提前警告他们?有什么好的工作流程策略需要记住吗?

编辑:我刚遇到git slave,在这种情况下也可能值得一看。除了其网站上的内容之外,还无法对能力/限制进行良好的评估。

4

2 回答 2

15

给其他人的一些注意事项!

新手会犯的最大错误是在完成子模块更新后,在子模块中使用分离的 HEAD 提交。我将尝试用来自钩子的强烈警告来解决这个问题。

下一个最大的可能是在进行需要的结帐后无法进行子模块更新。同样,钩子可以检查这一点并发出警告。

至于开发过程,这种设置使得在子模块中拥有良好的测试基础架构变得更加重要,这样如果可能的话,您可以在其中工作而不必在父模块中工作,并完全避免这个问题。

我将尝试从我最终使用的钩子中发布示例代码,并在一个月后跟进(希望不会太多)真实的恐怖故事。

编辑:

这是钩子的初稿。请记住,这是一项匆忙的工作,请放轻松!

在父仓库中:

对于合并后和结帐后,如果子模块不同步,我们会警告用户。(post-merge 尤其适用于快进合并,从原点拉取)还要注意他们会想要签出一个分支,尽管子模块的 post-checkout 钩子在他们运行子模块更新时也会这样做。提醒越多越好。

#!/bin/bash
if git submodule status | grep '^+' > /dev/null; then
    echo "WARNING: common model submodule now out of sync. You probably want to run" 1>&2
    echo "         git submodule update, then make sure to check out an appropriate branch" 1>&2
    echo "         in the submodule." 1>&2
fi

对于提交后,如果有子模块更改,我们会警告用户他们可能忘记将它们包含在提交中。在这种高度耦合的情况下,这是一个很好的猜测。用户不太可能分别修改模拟和通用模型

#!/bin/bash
if git submodule status | grep '^+' > /dev/null; then
    echo "WARNING: common model submodule has changes. If the commit you just made depends" 1>&2
    echo "         on those changes, you must run git add on the submodule, and then run" 1>&2
    echo "         git commit --amend to fix your commit." 1>&2
fi

在子模块中,一个 post-checkout 钩子强烈警告分离的 HEAD:

#!/bin/bash

get_ppid() {
    ps --no-headers -o ppid $1
}

# Check to see if this checkout is part of a submodule update
# git submodule calls git checkout, which calls this script, so we need to
# check the grandparent process.
if ps --no-headers -o command $(get_ppid $(get_ppid $$)) | grep 'submodule update' &> /dev/null; then
    if ! git symbolic-ref HEAD &> /dev/null; then
        echo "WARNING: common model submodule entering detached HEAD state. If you don't know" 1>&2
        echo "         what this means, and you just ran 'git submodule update', you probably" 1>&2
        echo "         want to check out an appropriate branch in the submodule repository." 1>&2
        echo
        # escape the asterisk from SO's syntax highlighting (it sees C comments)
        branches=($(git for-each-ref --format='%(objectname) %(refname:short)' refs/heads/\* | grep ^$(git rev-parse HEAD) | cut -d\  -f2))
        case ${#branches} in
            0 )
                ;;
            1 ) 
                echo "Branch '${branches[0]}' is at HEAD"
                ;;
            * )
                echo "The following branches are at HEAD: ${branches[@]}"
                ;;
        esac
    fi
    echo
fi

我还添加了一个预提交挂钩来简单地中止使用分离的 HEAD 进行的提交(除非它是一个变基)。我很害怕得到经典的“我所有的提交都消失了”的恐慌投诉。--no-verify如果你知道你在做什么,你总是可以绕过它。

于 2010-09-15T20:07:58.603 回答
8

子模块是确保引用所涉及的不同组件的精确修订的好选择。
正如我在“子模块的真实性质”中详述的那样,您仍然可以更新任何子模块,前提是您先提交它们(然后转到父仓库并提交)

但是,对于紧密耦合的模块,我会尽量避免:

“我想这会给模拟仓库的集成过程带来很多麻烦”。

我没有看到中央集成过程有效地工作:它应该只记录新的快速发展。
为此,任何希望推送任何内容的用户都需要先拉动并将他的更改重新设置在已经推送的任何新更改之上。
开发人员更倾向于解决任何冲突和/或询问他/她的同事在变基期间他/她必须处理的一些修改的来源。

这(拉,变基,推)并不总是可能的,因为:

  • 涉及“高级”(不太基本)Git 操作(以及与当前工作流程不完全相同的工作流程)
  • 涉及的工作(考虑到其他贡献者的演变)
  • 环境设置(最好设置一个额外的环境来制作那个变基,这又是“不那么基本”

但这仍然是我会尝试的方向。

(……但也许不只是在一个月假期之前;)
再说一次,谁休了一整个月的假期?!以前从未听说过这个概念)

于 2010-09-14T22:50:42.067 回答