在团队中实施no-ff合并

时间:2016-03-23 07:49:01

标签: git merge fast-forward

所以在工作中我们正在实施一个新的,不错的Git分支策略 - 真棒!

为了保留我们的存储库的新(和好)结构,我们希望所有合并都使用--no-ff标志(以及--no-commit标志来完成,以允许更好的合并提交消息)。但是,似乎要求每个人都记住它,有点不可靠。有没有办法强制每个开发人员必须与上述标志合并?

据我所知,用钩子检查这个是不可能的(因为git不能可靠地存储有关快进的任何信息)。我知道可以在每台开发者机器上设置配置(通过运行git config --global merge.ff no)。如果这是解决方案,我如何确保每个开发人员都设置了此配置?

3 个答案:

答案 0 :(得分:2)

我相信你可以在服务器上的预接收挂钩中检查这个,但是定义允许的内容很棘手,并且对于那些做这些事情的人来说可能会更难。而且,它只会给那些做错了事的人一个错误:由他们来修理它,这对某些用户来说可能有点复杂。

此外,您(和开发人员)可能不希望在某些合并上强制--no-ff:特别是,如果您在分支X上没有提交并且您获取新的origin/X提交,您可能希望将X快进到origin/X。 (另一方面,您/他们总是可以使用git rebase来处理此类情况。)

那就是说,让我们看看我们是否可以定义正确的行为。

首先,我们可能会注意到“快进”实际上是标签移动的属性,而不是合并的属性。当标签的先前提交是其新提交的祖先时,标签以快进方式移动。 (因此Git使用术语“快进合并”来指代实际上根本不是合并的东西。)git用于任何分支推送更新的默认测试是标签更新<除非设置了强制标志,否则em>必须是快进操作。

我们不想拒绝标签快进,因为这是扩展分支的正常情况,有或没有合并:

...--o--o--n--n--n   <-- br1

在此图中,我们表示建议的更新(如预接收挂钩中所示),其中现有提交写为o,新写入为n。标签br1(更确切地说,refs/heads/br1)曾用于指向最提示o,现在将指向最提示n

在您的预接收挂钩中,会发生的事实是存储库实际上已经已经更新并且有新的提交(合并与否),并且git只是向您发送每个引用更新请求,以<old-hash, new-hash, name>元组的形式。换句话说,给定上面的压缩br1更新图,我们可能使用大写和小写(我不能做颜色,唉)用大写字母表示传入的 old-hash new-hash 值,给出:

...--o--O--n--n--N   <-- br1

如果你拒绝推送,git会收集垃圾收集新的提交,因为你告诉它不允许任何引用更新(包括分支名称),所以br1结束仍然指向commit {{1 }}

现在,一般情况下合并在你的系统中很好,但你想要的是确保当O获得稍后将在br1上的合并提交时,分支br2移动以直接包含该合并提交。也就是说,这没关系:

br2

甚至都可以(可能 - 我们可能会假设您稍后会获得...--o--O--n--n--N <-- br1 ... / ...---o--O--n--N <-- br2 更新,我们会检查 部分然后;我们还不能做到,因为我们还没有得到br2更新:

br2

但这是被拒绝的:

...--o--O--n--n--N   <-- br1
...             /
...         n--n
...        /
...----o--o          <-- br2 (you get no update since br2 did not move)

这是这样的:

...--o--O--n--n--N   <-- br1, br2
...             /
...---o--O--n--n

另一方面,这是可以的(尽管我们可能希望约束我们允许在...--o--O--n--n--N <-- br1 ... / \ ...---o--O--n--n N <-- br2 上通过哪个父;可能在这些图中,直线指向左边是所有br2个链接):

--first-parent

此外,合并后可以获得额外的非合并提交:

...--o--O--n--n--N     <-- br1
...             / \
...---o--O--n--n---N   <-- br2

(同样在...--o--O--n--n--n--N <-- br1 ... / \ ...---o--O--n--n---N <-- br2 )。但是,我们必须检查每个合并,因为确定:

br2

(有人在...--o--O--n--n--n--n--n---N <-- br1 ... / \ \ / ...---o--O--n--n n--n--N <-- br2 时遇到git merge br2,然后br1git merge br1获得快进时做了br2,然后在br2做了两次提交;他们还在br1上进行了两次提交;然后他们再次将br1合并到br2,然后将br2合并为br1作为--no-ff合并;然后将br1br2推入一个git push)。

那么:我们应该执行什么规则呢?我认为,通过在遍历合并提交时强制执行有关--first-parent的规则,我们可以更轻松地使更容易。特别是,我们想要的是:

  • 给出分支更新(不是创建或删除)
  • 按图表顺序(--first-parent
  • 对{em> old-hash..new-hash 进行git rev-list --topo-order次遍历
  • 要求结果列表中最早的提交作为其第一个父项具有旧哈希。

有很多方法可以编写这个,尝试使用--boundary很有诱惑力,但这不能正常工作,因为合并显示的边界提交包括其所有父项,即使使用{ {1}}。所以,让我们选择最简单的方法:

--first-parent

请注意,这也会拒绝"foxtrot merges",因为第一个父级# Operation must be a fast-forward. This ensures that # $oldsha is an ancestor of (and thus related to) $newsha, # and thus we are not discarding any commits. if ! git merge-base --is-ancestor $oldsha $newsha; then ... reject as non-fast-forward fi edge=$(git rev-list --topo-order --first-parent $oldsha..$newsha | tail -1) # If the rev-list is empty then $oldsha is not related to $newsha. # However, we checked that first. (The only other case where this # can occur is if $oldsha equals $newsha, which is not an update, # so we won't be running this code at all.) # # The listed edge commit may, however, be a root commit (have # no parent). We must reject this case as well as "parent is # not $oldsha". Fortunately that happens automatically since # the empty string does not match a valid hash; we just need # to be sure to quote "$parent". parent=$(git rev-parse -q --verify $edge^) if [ "$parent" = $oldsha ]; then ... update is OK else ... reject as containing a bogus merge fi 不会返回原始哈希值。

(我没有测试过这些。)

答案 1 :(得分:0)

这里有一个示例钩子代码:

#!/bin/sh

# for details see here, 
# http://git-scm.com/book/en/Customizing-Git-An-Example-Git-Enforced-Policy
# it seems that git on Windows doesn't support ruby, so use bash instead
# to function, put it into remote hook dir
# to disable, rename or delete file in remote hook dir

refname=$1
oldrev=$2
newrev=$3


# enforces fast-forward only pushes
check_fast_forward ()
{
  all_refs=`git rev-list ${oldrev}..${newrev} | wc  -l`
  single_parent_refs=`git rev-list ${oldrev}..${newrev} --max-parents=1 | wc  -l `
  if [ $all_refs -eq $single_parent_refs ]; then
    echo "This is the section for fast-forward commits ..."
    exit 0
  fi
}

check_fast_forward

根据您的需要进行设置:

  

-eq 等于

if [ "$a" -eq "$b" ]
     

     

-ne 不等于

if [ "$a" -ne "$b" ]

答案 2 :(得分:0)

由于几乎任何选项都会让团队成员在git设置中执行额外的步骤,所以最好不要让每个人将其全局配置设置为默认为--no-ff?

唯一使用ff的是那些明确说明它的人,而不是那些忘记使用-no-ff的人。