让我们假设我有以下本地存储库和一个像这样的提交树:
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
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/)//';
母体名
基于git show-branch -a加上一些过滤器的解决方案有一个缺点:git可能会考虑存在时间较短的分支的分支名。
如果你有一些你关心的潜在父母,你可以问自己类似的问题(可能也是OP想知道的):
从所有分支的特定子集,哪个是最近的父git分支?
为了简化起见,我将“一个git分支”表示HEAD(即当前分支)。
让我们假设我们有以下分支:
HEAD
important/a
important/b
spam/a
spam/b
基于git show-branch -a +过滤器的解决方案,可能会给出最近的HEAD父节点是spam/a,但我们不关心这个。
如果我们想知道important/a和important/b中哪一个是HEAD的最近父节点,我们可以运行下面的代码:
for b in $(git branch -a -l "important/*" | sed -E -e "s/\*//"); do
d1=$(git rev-list --first-parent ^${b} HEAD | wc -l);
d2=$(git rev-list --first-parent ^HEAD ${b} | wc -l);
echo "${b} ${d1} ${d2}";
done \
| sort -n -k2 -k3 \
| head -n1 \
| awk '{print $1}';
它的作用:
1.) $(git branch -a -l "important/*" | sed -E -E "s/\*//"):打印带有某些模式("important/*")的所有分支列表。(如果你碰巧在一个重要的/*分支上,git分支将包含一个*来指示你当前的分支。命令替换$()将把它展开到当前目录的内容中。sed从git分支的输出中删除*。)
2.) d=$(git revl -list——first-parent ^${b} HEAD | wc -l);:对于每个分支($b),计算从HEAD到$b中最近的提交的距离($d1)(类似于计算从一点到直线的距离)。在这里,您可能需要以不同的方式考虑距离:您可能不想使用——first-parent,或者可能需要从分支尖端到尖端的距离("${b}"…HEAD),…
2.2) d2=$(git rev-list——first-parent ^HEAD ${b} | wc -l);:对于每个分支($b),计算从分支顶端到HEAD中最近的提交的距离($d2)。我们将使用这个距离在距离$d1相等的两个分支之间进行选择。
3.) echo "${b} ${d1} ${d2}";:打印每个分支的名称,后面跟着距离,以便稍后对它们进行排序(首先是$d1,然后是$d2)。
4.) | sort -n -k2 -k3:对之前的结果进行排序,因此我们得到一个排序(按距离)的所有分支的列表,后面跟着它们的距离(两个)。
5) | head -n1:上一步的第一个结果将是距离较小的分支,即最近的父分支。所以丢弃所有其他分支。
6.) | awk '{print $1}';:我们只关心分支的名称,而不关心距离,因此提取第一个字段,这是父节点的名称。在这里!:)