让我们假设我有以下本地存储库和一个像这样的提交树:
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,以确定该特性是否需要重基。
一个rephrasal
这个问题的另一种表达方式是“驻留在当前分支以外的分支上的最近的提交是什么?是哪个分支?”
一个解决方案
您可以使用一点命令行魔法找到它
git show-branch \
| sed "s/].*//" \
| grep "\*" \
| grep -v "$(git rev-parse --abbrev-ref HEAD)" \
| head -n1 \
| sed "s/^.*\[//"
AWK:
git show-branch -a \
| grep '\*' \
| grep -v `git rev-parse --abbrev-ref HEAD` \
| head -n1 \
| sed 's/[^\[]*//' \
| awk 'match($0, /\[[a-zA-Z0-9\/.-]+\]/) { print substr( $0, RSTART+1, RLENGTH-2 )}'
下面是它的工作原理:
Display a textual history of all commits, including remote branches.
Ancestors of the current commit are indicated by a star. Filter out everything else.
Ignore all the commits in the current branch.
The first result will be the nearest ancestor branch. Ignore the other results.
Branch names are displayed [in brackets]. Ignore everything outside the brackets, and the brackets.
Sometimes the branch name will include a ~# or ^# to indicate how many commits are between the referenced commit and the branch tip. We don't care. Ignore them.
结果是
运行上面的代码
A---B---D <-master
\
\
C---E---I <-develop
\
\
F---G---H <-topic
如果你从H运行它会给你发展,如果你从I运行它会给你掌握。
代码可以作为要点提供。
由于之前的答案在我们的存储库中都不起作用,我想分享我自己的方法,使用git日志中的最新归并:
#!/bin/bash
git log --oneline --merges "$@" | grep into | sed 's/.* into //g' | uniq --count | head -n 10
把它放在一个名为git-last-merge的脚本中,该脚本也接受一个分支名称作为参数(而不是当前分支)以及其他git日志参数。
从输出中,我们可以根据自己的分支约定和每个分支的合并数量手动检测父分支。
如果你经常在子分支上使用git rebase(合并通常是快进的,所以没有太多的合并提交),这个答案不会很好地工作,所以我写了一个脚本来计算所有分支与当前分支相比的前提交(正常和合并)和后提交(在父分支中不应该有任何后合并)。
#!/bin/bash
HEAD="`git rev-parse --abbrev-ref HEAD`"
echo "Comparing to $HEAD"
printf "%12s %12s %10s %s\n" "Behind" "BehindMerge" "Ahead" "Branch"
git branch | grep -v '^*' | sed 's/^\* //g' | while read branch ; do
ahead_merge_count=`git log --oneline --merges $branch ^$HEAD | wc -l`
if [[ $ahead_merge_count != 0 ]] ; then
continue
fi
ahead_count=`git log --oneline --no-merges $branch ^$HEAD | wc -l`
behind_count=`git log --oneline --no-merges ^$branch $HEAD | wc -l`
behind_merge_count=`git log --oneline --merges ^$branch $HEAD | wc -l`
behind="-$behind_count"
behind_merge="-M$behind_merge_count"
ahead="+$ahead_count"
printf "%12s %12s %10s %s\n" "$behind" "$behind_merge" "$ahead" "$branch"
done | sort -n
Joe Chrysler的命令行魔法可以简化。下面是Joe的逻辑——为了简洁起见,我在两个版本中都引入了一个名为cur_branch的参数来代替命令替换' git rev-parse——abbrev-ref HEAD ';可以像这样初始化:
cur_branch=$(git rev-parse --abbrev-ref HEAD)
然后,这是Joe的管道:
git show-branch -a |
grep '\*' | # we want only lines that contain an asterisk
grep -v "$cur_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
我们可以在一个相对简单的awk命令中完成与所有这五个单独的命令过滤器相同的事情:
git show-branch -a |
awk -F'[]^~[]' '/\*/ && !/'"$cur_branch"'/ {print $2;exit}'
具体情况是这样的:
-F'[]^~[]'
将行分割为以]、^、~和[为字符的字段。
/\*/
找出包含星号的行
&& !/'"$cur_branch"'/
...但不是当前的分支名称
{ print $2;
当您找到这样的一行时,打印它的第二个字段(即字段分隔符第一次和第二次出现之间的部分)。对于简单的分支名称,这将是括号之间的内容;对于具有相对跳转的引用,它将只是没有修饰符的名称。因此,我们的字段分隔符集处理了两个sed命令的意图。
exit }
然后立即退出。这意味着它只处理第一个匹配的行,所以我们不需要通过head -n 1来输出。