所以在工作中我们正在实施一个新的,不错的Git分支策略 - 真棒!
为了保留我们的存储库的新(和好)结构,我们希望所有合并都使用--no-ff
标志(以及--no-commit
标志来完成,以允许更好的合并提交消息)。但是,似乎要求每个人都记住它,有点不可靠。有没有办法强制每个开发人员必须与上述标志合并?
据我所知,用钩子检查这个是不可能的(因为git不能可靠地存储有关快进的任何信息)。我知道可以在每台开发者机器上设置配置(通过运行git config --global merge.ff no
)。如果这是解决方案,我如何确保每个开发人员都设置了此配置?
答案 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
,然后br1
在git merge br1
获得快进时做了br2
,然后在br2
做了两次提交;他们还在br1
上进行了两次提交;然后他们再次将br1
合并到br2
,然后将br2
合并为br1
作为--no-ff
合并;然后将br1
和br2
推入一个git push
)。
那么:我们应该执行什么规则呢?我认为,通过在遍历合并提交时强制执行有关--first-parent
的规则,我们可以更轻松地使和更容易。特别是,我们想要的是:
--first-parent
)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的人。