假设远程存储库有一个开发分支的副本(您最初的描述在本地存储库中描述了它,但听起来它也存在于远程存储库中),您应该能够实现我认为您想要的东西,但是方法与您预想的有点不同。
Git的历史记录基于提交的DAG。分支(和一般的“引用”)只是在不断增长的提交DAG中指向特定提交的临时标签。因此,分支之间的关系可以随时间变化,但提交之间的关系不会。
---o---1 foo
\
2---3---o bar
\
4
\
5---6 baz
看起来baz是基于(旧版的)bar?但是如果我们删除bar呢?
---o---1 foo
\
2---3
\
4
\
5---6 baz
现在看起来baz是基于foo的。但是巴兹的祖先并没有改变。我们只是删除了一个标签(以及由此产生的悬空提交)。如果我们在4处添加一个新标签呢?
---o---1 foo
\
2---3
\
4 quux
\
5---6 baz
现在看来baz是基于quux的。尽管如此,祖先并没有改变,只是标签变了。
然而,如果我们问“commit 6是commit 3的后代吗?”(假设3和6是完整的SHA-1提交名称),那么无论bar和quux标签是否存在,答案都是“yes”。
因此,您可以问这样的问题:“推送的提交是开发分支当前尖端的后代吗?”,但你不能可靠地问“推送的提交的父分支是什么?”。
一个似乎接近你想要的最可靠的问题是:
对于所有被推送的提交的祖先(不包括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
上面的代码可以在接受H'、J、K或N的同时拒绝手牌S,但它也可以接受L和P(它们涉及合并,但它们不合并develop的尖端)。
要拒绝L和P,你可以改变问题,然后问
对于所有被推送的提交的祖先(不包括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_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