让我们假设我有以下本地存储库和一个像这样的提交树:
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
我不喜欢解析半结构化文本输出时涉及的不安全假设,所以我想要一个更健壮的解决方案,假设更少:
# Search backwards in history for the first commit that is in a branch other than $1
# and output that branch's name.
parent_branch() {
local result rev child_branch=$1
rev=$(git rev-parse --revs-only $child_branch)
while [[ -n $rev ]]; do
result=$(git branch --contains $rev | grep -v " $child_branch$")
if [[ -n $result ]]; then
echo $result
return 0
fi
rev=$(git rev-parse --revs-only $rev^)
done
return 1
}
注意:由于这是在历史上进行迭代,查看每个提交以找到第一个在不同分支中而不是$1的提交,因此分支越长,它的开销就越高。但是,由于通常情况下分支应该是相对短暂的,所以这不应该是一个太大的问题。
还要注意,我使用的是git branch——contains,所以这也会找到共享公共基础但已经超越它的分支。要只找到确切指向公共基的分支,请使用git branch——points-at。
git log -2 --pretty=format:'%d' --abbrev-commit | tail -n 1 | sed 's/\s(//g; s/,/\n/g';
(来源/母体名,母体名)
git log -2 --pretty=format:'%d' --abbrev-commit | tail -n 1 | sed 's/\s(//g; s/,/\n/g';
起源- parent-name
git log -2 --pretty=format:'%d' --abbrev-commit | tail -n 1 | sed 's/(.*,//g; s/)//';
母体名