我需要生成一个报告,其中包含从develop
分支(过去某个时候从master
分支出来)创建的所有分支名称。我的方法是使用develop
获取git log --pretty=format:"%h" master..develop
中的第一次提交和最后一次提交,然后使用git branch --contains <hash>
使用提交范围。
但是,上述方法存在一些问题:
git branch --contains
命令。git branch --contains
命令的输出将包含冗余结果,因为大多数提交在分支之间是常见的。我希望有一种更好,更直接的方法。下面,我将尝试说明感兴趣的情况:
master
^
| develop
a---b ^
\ |
--c--d---e---f---g--h--i
\ \ \ \
k-l m n o--p
^ ^ ^ ^
| | | |
branch_1 | branch_3 |
branch_2 branch_4
// Desired output:
branch_1
branch_2
branch_3
branch_4
答案 0 :(得分:4)
您可以使用一个命令完全关闭:
git log --ancestry-path --branches --not $(git merge-base --all master develop) \
--pretty=%D --simplify-by-decoration --decorate-refs=refs/heads
那是“向我展示所有分支尖端历史中的分支装饰,这些分支装饰可追溯到master和development的当前合并基础。”
您可以通过类似awk '{print $NF}' RS=', |\n'
之类的结果来整理结果,使其更漂亮。
请注意,此操作(如@torek的回答)假定您正在为生产级稳定性(如生产主数据库)管理的回购中执行此操作,分支名称和提交之间根本没有必要的关系,更不用说永久的了,或者全球性或独特性。如果您尝试将提交永久绑定到某个外部管理记录,请在提交消息中执行此操作,而不要在分支名称中执行。
编辑:如果您想快速了解自从主开发之后的当前分支结构,
git log --graph --decorate-refs=refs/heads --oneline \
--branches --simplify-by-decoration \
--ancestry-path --boundary --not `git merge-base master develop`
答案 1 :(得分:3)
分支名称仅指向一次提交。通过从该点向后浏览图形,可以找到属于该分支的所有早期提交。例如,master..develop
就是这样工作的:它选择develop
祖先的提交,不包括master
祖先的所有提交。
(请注意,在这种情况下:
o--o <-- master
/
...--o--o
\
*--*--* <-- develop
两点master..develop
语法包括三个加星标的提交。)
我认为您要的是:
for each branch name $branch:
if $branch identifies a commit that is a descendant of any
of the commits in the set `master..develop`:
print $branch
但是,根据定义,属于提交i
的任何提交都是提交c
的后代。因此,就像tkruse notes in a comment:
git branch --contains <hash-of-c>
就足够了。要在此处找到要使用的提交,可以运行git rev-list --topo-order master..develop | tail -1
。 (如果您使用--reverse
和head -1
可能会导致管道破裂,因此tail -1
方法会更好。烦人的是,您无法将--reverse
与{{1}组合使用}。
您可能会担心这种情况下的故障:
-n 1
您可能希望包含 o--o <-- master
/
...--o--o--1--o--o <-- branch-X
\ \
2--*--* <-- develop
\
o <-- branch-Y
和branch-X
,并且由于它们是两个的后代,因此无法通过使用一个branch-Y
操作来找到它们在git branch --contains
范围内提交,在此表示为master..develop
和1
。在这里,您确实确实需要多个2
-es,或者:
git branch --contains
(未经测试,到目前为止已修复至少一个错误)。但这比简单的set -- $(git rev-list master..develop)
git for-each-ref --format='%(refname)' refs/heads | while read ref; do
branch=${ref#refs/heads/}
tip=$(git rev-parse $ref)
take=false
for i do
if git merge-base --is-ancestor $i $tip; then
take=true
break
fi
done
$take && echo $branch
done
测试要慢得多。此处的逻辑是确定rev-list输出中的任何提交哈希ID是否是所讨论分支的尖端的祖先。
(请注意,这将打印git branch --contains
-develop
认为提交是其自己的祖先,因此您可能要显式地跳过该提交。)
(您可以通过找到 just 适当的基本提交来加快此速度。例如,在上面的示例中,只有两个。您可以在这些示例上使用git merge-base --is-ancestor
并合并命名的分支。要使用的基数取决于--contains
中任何合并提交的数量和结构。可以使用master..develop
来找到 just 边界提交;我还没有尝试过。)
我在午餐时意识到,有一种简便的方法可以找到用于git rev-list --boundary
或--contains
测试的最小提交集。从范围内的提交的完整列表开始,并按父级先后的拓扑顺序进行排序,例如:
--is-ancestor
然后从“我们必须检查的承诺”的空列表开始:
set -- $(git rev-list --reverse --topo-order master..develop)
现在,对于所有可能的哈希列表中的每个哈希ID,测试每个哈希ID,例如:
hashes=
其中if ! is_descendant $i $hashes; then
hashes="$hashes $i"
fi
是:
is_descendant
循环完成后,# return true (i.e., 0) if $1 is a descendant of any of
# $2, $3, ..., $n
is_descendant() {
local i c=$1
shift
for i do
# $i \ancestor $c => $c \descendant $i
git merge-base --is-ancestor $i $commit_to_test && return 0
done
return 1
}
包含可用于$hashes
或--contains
测试的最小提交集。