- 原文地址:Protect our Git Repos, Stop Foxtrots Now!
- 原文做者:Sylvie Davies
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:LeviDing
- 校对者:薛定谔的猫,luisliuchao
舞者们正准备跳狐步舞。html
“狐步舞”式的合并是 git commit
的一个特别很差的具体顺序。如同在户外看到的“狐步舞”,这种commits序列像这个样子:前端
但在公开场合不多会见到“狐步舞”。它们隐藏在树冠之间,树枝之间。我称它们“狐步舞”式,是由于他们交叉的样子,他们看起来像同步舞蹈的舞步顺序:react
还有一些人也提到“狐步舞”式的合并,但它们历来没有直接说出它的名字。例如,Junio C. Hamano 的博客有有趣的 --first-parent
,还有有趣的非快进方式(Non-Fast-Forward)。David Lowe 的 nestoria.com 有关于保持一致的线性历史记录的文章。此外还有一大堆人告诉你要避免使用 git pull
,而是使用 git pull –rebase
。为何?主要是为了不通常的合并和提交时的错误,此外还能够避免出现该死的“狐步舞”式的提交。linux
“狐步舞”式的合并真的很很差吗?是的。android
它们显然不如僧帽水母那样糟糕。可是“狐步舞”式的合并也是很差的,你不但愿你的 git 仓库里有它们的身影。ios
“狐步舞”式的合并很差,由于它会改变 origin/master
分支的“第一父级”的地位。git
合并提交记录的父级是有序的。第一个父级是 HEAD
。第二个父级是用 git merge
命令提交的。github
你能够像下面这样想:后端
git checkout 1st-parent
git merge 2nd-parent
复制代码
若是你是 octopus 的说客:bash
git merge 2nd-parent 3rd-parent 4th-parent ... 8th-parent etc...
复制代码
这意味着父级的记录就像它听起来同样。当你提交新的代码的时候,忽略第一个父级之外的父级,从而获得一个新的代码记录。对于常规的 commit
(非 merge
),第一个父级是惟一的父级,而且对于 merge
来讲,它是你在输入 git merge
时所产生的记录。这种父级概念是直接植入到 Git 里的,而且在不少命令行中都有所体现,例如,git log –-first-parent
。
“狐步舞”式的合并问题在于,它使得 origin/master 由第一父级变成了第二父级。
除了 Git 在评估提交是否有资格进行 fast-forward
时,Git 并不关心父级的前后次序。
固然你很不但愿这样。你不但愿“狐步舞”式的合并经过 fast-forward
的方式更新你的 origin/master,使得 origin/master 第一父级的地位不稳定。
看一下当“狐步舞”式的合并被 push
上去的时候会发生什么:
可使用手指从 origin/master 开始沿着图形往下,在每一个分叉的地方选择左边的分支,从而知道当前的第一父级的变动历史。
问题是,最初的第一个父级提交次序(从 origin/master 开始)是这样的:
B, A.
可是当“狐步舞”式的合并被 push
以后,父级的次序变成这样了:
D, C, A.
这时,B 节点已从 origin/master 第一父级中消失,事实上,B在它的第二父级上。固然,不会有任何资料的丢失,而且 B 节点仍然是 origin/master 的一部分。
可是,这样父级节点就会有错综复杂的关系。你是否知道,tilda
符号(例如 ~N
)指定从第 N 个提交的节点到第一个父节点间的路径?
你有没有想要看看你的分支上的每一个提交记录之间的差别,可是使用 git log -p
显然会漏掉一些信息,使用 git log -p -m
能获取更多的信息吗?
尝试使用 git log -p -m –first-parent
吧。
你想过要还原一个合并的分支吗?那你须要为 git revert
提供 -m parent-number
选项,这时候你就很不但愿本身提供的 parent-number
是错的。
和我一块儿工做的人,大多数都将第一个父级做为真正的 master
分支。有意识或无心识地,人们将 git log –first-parent origin/master
视为重要事物的顺序。 至于任何其余合并进来的分支?嗯,你应该知道他们会怎么说:
可是“狐步舞”式的合并把这些都混在了一块儿。请考虑下面的例子,其中 origin/master 分支的一系列的重要提交信息,与你本身的稍微不那么重要的提交并行:
如今,你终于准备把你的工做并入到 master
中。你输入 git pull
,或者可能你在一个主题分支上使用 git merge master
命令。那这样发生了什么?一个“狐步舞”式的合并就这么出现了。
一切都没有什么大问题,除了当你键入 git push
,让你的远程仓库接受它时,你的历史记录看起来像这样:
啥招都没有,随它们去吧。除非你重写 master 分支的历史而惹怒其余人,那么就去这么疯吧。
事实上,不要这样作。
这有几个方法。我最喜欢的的方式是下面的四步:
为你的团队安装 Atlassian Bitbucket 服务器。
安装我为 Bitbucket 服务器写的插件,名字叫“Bit Booster Commit Graph and More”。 你能够在下面的连接中找到他们:marketplace.atlassian.com/plugins/com…marketplace.atlassian.com/plugins/com…
在你全部项目中,都点击 “Protect First Parent Hook” 上的 “Enabled” 按钮,也就是“启用”按钮:
这是我最喜欢的方式,由于它杜绝了“狐步舞”的出现。每当有一个“狐步舞”式的合并被阻挡时,它会打印一只牛:
$ git commit -m 'my commit'
$ git pull
$ git push
remote: _____________________________________________
remote: / \
remote: | Moo! Your bit-booster license has expired! |
remote: \ /
remote: ---------------------------------------------
remote: \ ^__^
remote: \ (oo)\_______
remote: (__)\ )\/\
remote: ||----w |
remote: || ||
remote:
remote: *** PUSH REJECTED BY Protect-First-Parent HOOK ***
remote:
remote: Merge [da75830d94f5] is not allowed. *Current* master
remote: must appear in the 'first-parent' position of the
remote: subsequent commit.
复制代码
还有其余的方法。你能够禁止直接向 master 分支进行推送,并保证不在 fast-forward
的状况下合并 pull-requests
。或者培训你的员工使用 git pull –rebase
命令,而且永远不要使用 git merge master
。而且一旦你培训完你的员工,就不要再招聘其余员工了。
若是你能够直接访问远程仓库,则能够设置 pre-receive hook
。 如下的 bash
脚本能够帮助你开始这项设置:
#/bin/bash
# Copyright (c) 2016 G. Sylvie Davies. http://bit-booster.com/
# Copyright (c) 2016 torek. http://stackoverflow.com/users/1256452/torek
# License: MIT license. https://opensource.org/licenses/MIT
while read oldrev newrev refname
do
if [ "$refname" = "refs/heads/master" ]; then
MATCH=`git log --first-parent --pretty='%H %P' $oldrev..$newrev |
grep $oldrev |
awk '{ print \$2 }'`
if [ "$oldrev" = "$MATCH" ]; then
exit 0
else
echo "*** PUSH REJECTED! FOXTROT MERGE BLOCKED!!! ***"
exit 1
fi
fi
done
复制代码
push
上去。我该怎么解决?假设你安装了预先接收的钩子,而且阻止你“狐步舞”式的合并。你下一步作什么?你有三种可能的补救办法:
rebase
:但请不要使用上面的第三种方法,由于最后的结果被称为“僧帽水母”式的合并,这种合并甚至比“狐步舞”式的合并更糟糕。
在最后,其实“狐步舞”式的合并也像其余的合并那样。两个(或多个)提交到一块儿融合成一个新的记录节点。就你的代码库而言,没有任何区别。不管 commit A 合并到 commit B 中仍是反过来 commit B 合并到 commit A,从代码的角度来看最终结果是相同的。
可是,当涉及到你的仓库的历史记录时,以及有效地使用 git 工具集时,“狐步舞”式的合并会有必定的破坏性。经过设置相应的策略来防止其出现,可使你仓库的历史记录更加清晰明了,并减小了须要记住的 git 命令选项的范围。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、React、前端、后端、产品、设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划。