让我们假设我有以下本地存储库和一个像这样的提交树:
master --> a
\
\
develop c --> d
\
\
feature f --> g --> h
Master是我的,这是最新的稳定发布代码,develop是我的,这是“下一个”发布代码,feature是一个正在准备开发的新功能。
使用钩子,我希望能够拒绝推送功能到我的远程存储库,除非commit f是develop HEAD的直接后代。也就是说,提交树看起来是这样的,因为feature已经基于d。
master --> a
\
\
develop c --> d
\
\
feature f --> g --> h
那么是否有可能:
识别特征的父分支?
确定父分支中的提交f是哪个分支的后代?
从那里,我将检查父分支的HEAD是什么,并查看f前任是否匹配父分支HEAD,以确定该特性是否需要重基。
在多个分支中寻找第一次提交的shell函数:
# Get the first commit hash of a given branch.
# Uses `git branch --contains` to backward (starts from HEAD) check each commits
# and output that branch's name.
first_commit_of_branch() {
if [ $# -ne 1 ] || [ -z "${1}" ] ; then
(>&2 echo "Error: Missing or empty branch name.")
(>&2 echo "Usage: $0 branch_to_test")
return 2
fi
local branch_to_test="${1}"; shift
local commit_index_to_test
local maximum_number_of_commit_to_test
local branch_count_having_tested_commit
git rev-parse --verify --quiet "${branch_to_test}" 2>&1 > /dev/null || {
(>&2 echo "Error: Branch \"${branch_to_test}\" does not exists.")
return 2
}
commit_index_to_test=0
maximum_number_of_commit_to_test=$(git rev-list --count "${branch_to_test}")
while [ ${commit_index_to_test} -le ${maximum_number_of_commit_to_test} ] ; do
# Testing commit $branch_to_test~$commit_index_to_test…
# If it fails, it means we tested all commits from the most recent of
# branch $branch_to_test to the very first of the git DAG. So it must be it.
git rev-parse --verify --quiet ${branch_to_test}~${commit_index_to_test} 2>&1 > /dev/null || {
git rev-list --max-parents=0 "${branch_to_test}"
return 0
}
branch_count_having_tested_commit="$( \
git --no-pager branch --no-abbrev --verbose \
--contains ${branch_to_test}~${commit_index_to_test} \
| cut -c 3- \
| cut -d ' ' -f 2 \
| wc -l \
)"
# Tested commit found in more than one branch
if [ ${branch_count_having_tested_commit} -gt 1 ] ; then
if [ ${commit_index_to_test} -eq 0 ]; then
(>&2 echo "Error: The most recent commit of branch \"${branch_to_test}\" (${branch_to_test}~${commit_index_to_test}) is already in more than one branch. This is likely a new branch without any commit (yet). Cannot continue.")
return 1
else
# Commit $branch_to_test~$commit_index_to_test is in more than
# one branch, stopping there…
git rev-parse ${branch_to_test}~$((commit_index_to_test-1))
return 0
fi
fi
# else: Commit $branch_to_test~$commit_index_to_test is still only in
# branch ${branch_to_test} continuing…"
commit_index_to_test=$((commit_index_to_test+1))
done
}
警告:当在一个有子分支的分支上执行并且没有新的提交时,它会失败。
A---B---C---D <- "main" branch
\ \
\ E---F <- "work1" branch
\ \
\ G---H <- "work1-b" branch
\
I---J <- "work2" branch
first_commit_of_branch main # C
first_commit_of_branch work1 # (Fails)
first_commit_of_branch work1-b # G
first_commit_of_branch work2 # I
Mark Reed的解决方案基本上是正确的。但是,请注意,提交行不仅应该包含星号,而且应该以星号开头!否则,包含星号的提交消息也包含在匹配的行中。所以它应该是:
git show-branch——| awk - f '[]^~[]' '/^\*/ && !/'"$ current_branch”/{打印2美元;退出}'
或者是更长的版本:
git show-branch -a |
awk '^\*' | # we want only lines that contain an asterisk
awk -v "$current_branch" | # but also don't contain the current branch
head -n1 | # and only the first such line
sed 's/.*\[\(.*\)\].*/\1/' | # really, just the part of the line between []
sed 's/[\^~].*//' # and with any relative refs (^, ~n) removed`