假设我有以下本地存储库,其提交树如下:
master --> a \ \ develop c --> d \ \ feature f --> g --> h
master这是我的 这是最新的稳定版本代码 ,这develop是我 的“下一个”版本代码 ,并且feature是 正在准备的新功能develop。
master
develop
feature
使用钩子,我希望能够拒绝推送到feature我的远程存储库,除非提交f是developHEAD 的直接后代。即,提交树看起来像这样,因为 feature has been git rebaseon d。
f
git rebase
d
那么是否有可能:
从那里我会检查父分支的 HEAD 是什么,并查看f前任是否与父分支 HEAD 匹配,以确定是否需要重新设置该功能。
假设远程仓库有一个 develop 分支的副本(你最初的描述是在本地仓库中描述的,但听起来好像远程也有),你应该可以实现我认为你想要的,但是方法和你想象的有点不同。
Git 的历史基于提交的DAG。分支(以及一般的“efs”)只是指向不断增长的提交 DAG 中特定提交的临时标签。因此,分支之间的关系可能会随着时间而变化,但提交之间的关系不会。
---o---1 foo \ 2---3---o bar \ 4 \ 5---6 baz
看起来baz是基于(旧版本的)bar?但是如果我们删除bar呢?
baz
bar
---o---1 foo \ 2---3 \ 4 \ 5---6 baz
现在看起来baz是基于foo. 但血统baz并没有改变。我们刚刚删除了一个标签(以及由此产生的悬空提交)。如果我们在 处添加一个新标签4呢?
foo
4
---o---1 foo \ 2---3 \ 4 quux \ 5---6 baz
现在看起来baz是基于quux. 尽管如此,血统并没有改变,只是标签发生了变化。
quux
然而,如果我们问“s commit 6a descendent of commit 3?”(假设3和6是完整的 SHA-1 提交名称),那么答案将是“测”,无论bar和quux标签是否存在。
6
3
因此,您可以问诸如“推送的提交是 开发 分支的当前提示的后代?”之类的问题,但您不能可靠地问“推送的提交的父分支是什么?”。
一个似乎接近您想要的最可靠的问题是:
对于所有已推送的提交的祖先(不包括 develop 的当前尖端及其祖先),将当前的 develop 尖端作为父级: 至少存在一个这样的提交吗? 所有这些提交都是单亲提交吗?
对于所有已推送的提交的祖先(不包括 develop 的当前尖端及其祖先),将当前的 develop 尖端作为父级:
这可以实现为:
pushedrev=... basename=develop if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then echo "'$basename' is missing, call for help!" exit 1 fi parents_of_children_of_base="$( git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" | grep -F "$baserev" )" case ",$parents_of_children_of_base" in ,) echo "must descend from tip of '$basename'" exit 1 ;; ,*\ *) echo "must not merge tip of '$basename' (rebase instead)" exit 1 ;; ,*) exit 0 ;; esac
这将涵盖您想要限制的一些内容,但可能不是所有内容。
作为参考,这里是一个扩展的示例历史:
A master \ \ o-----J \ / \ \ | o---K---L \ |/ C--------------D develop \ |\ F---G---H | F'--G'--H' | |\ | | o---o---o---N \ \ \ \ \ \ o---o---P \ \ R---S
上面的代码可以用来拒绝HandS同时接受H', J, K, or N,但它也可以接受Land P(它们涉及合并,但它们不合并 develop 的提示)。
H
S
H'
J
K
N
L
P
要也拒绝Land P,您可以更改问题并询问
对于所有被推送的提交的祖先(不包括当前的 开发 技巧及其祖先): 有两个父母的承诺吗? 如果没有,是否至少有一个这样的提交具有 开发 其(唯一)父级的当前提示?
对于所有被推送的提交的祖先(不包括当前的 开发 技巧及其祖先):
pushedrev=... basename=develop if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then echo "'$basename' is missing, call for help!" exit 1 fi parents_of_commits_beyond_base="$( git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" | grep -v '^commit ' )" case "$parents_of_commits_beyond_base" in *\ *) echo "must not push merge commits (rebase instead)" exit 1 ;; *"$baserev"*) exit 0 ;; *) echo "must descend from tip of '$basename'" exit 1 ;; esac