【发布时间】:2019-06-04 23:46:11
【问题描述】:
是否有工具可以显示特定分支已合并到哪些分支?例如,如果“A”已合并为“B”和“C”,但没有合并为“D”,如何输出“B”和“C”?我只需要分支名称。
【问题讨论】:
是否有工具可以显示特定分支已合并到哪些分支?例如,如果“A”已合并为“B”和“C”,但没有合并为“D”,如何输出“B”和“C”?我只需要分支名称。
【问题讨论】:
如果您指的是图形工具,gitk 似乎是一个不错的起点。
或者,您可能更喜欢对某些需求非常有用的 CLI,试一试:
git log --oneline --decorate --simplify-by-decoration --graph --all
更具体地解决“如何知道哪些分支合并到分支 A?”的问题
git branch --contains A
【讨论】:
git branch --contains A 类似的输出,但表明 A 先被合并到 B 中,然后 B 被合并到 C 中? (目前会分别显示B和C)
gitk 实际上非常有用,如果您可以通过相当过时的界面。您是否尝试过并出于特定原因排除了它?
重要的是要意识到分支名称实际上并没有任何意义。
分支名称只是一个标签或指针,指向一个提交。将它们视为黄色便签之一,甚至是sticky arrows,您可以将其粘贴到提交上。重要的是提交。提交的标识是它的哈希 ID。提交是永久的1 且不可更改,并且每个提交记录其直接父级(大多数提交)或父级(合并提交为 2 个或更多)的哈希 ID。这就是生成提交图的原因,它——除了你总是可以添加到它之外,它与提交本身一样永久且不可更改。
是图形本身决定了存储库的实际“分支”:
(older commits towards the left, newer ones towards the right)
o--A
/
...--o--o
\
o--o--B
图表的这个特定部分显示了两个分支,以两个提示提交A 和B 结束。在A 之后添加一个新的提交给我们C,它会记住A 作为它的父级:
o--A--C
/
...--o--o
\
o--o--B
如果我们现在添加一个 merge 提交——我们称它为 M 表示“合并”——它的父母是 both C和 B,我们得到:
o--A--C
/ \
...--o--o M
\ /
o--o--B
需要两个标签——两个便笺箭头——来记住A和B,或者同时记住C和B,但是现在我们有了M,我们可以处理其中一个这些标签,只保留一个指向 M 本身的便签箭头:
o--A--C
/ \
...--o--o M <-- branch
\ /
o--o--B
所以这就是分支名称的作用:它是一个指针,指向一个提交,Git会认为这是一些向后的tip提交- 看起来的提交链。随着 Git 向后工作,从这个提示提交开始,Git 将遍历合并的 所有 分支,以找到所有可以通过向后工作到达的提交。由于M 有两个父母C 和B,Git 将访问commit C 然后A 等等,还有B 然后所有commits 到B 的左边。
当然,我们可以让标签指向A 和/或B 和/或C:
.... <-- tag: v1.0
.
.
o--A--C
/ \
...--o--o M <-- branch
\ /
o--o--B <-- feature
如本图所示,标签不必是分支名称。分支名称的特殊功能是,通过使用git checkout <em>name</em>“进入”分支,我们所做的任何 new 提交都会自动更新名称以指向新提交。新的提交将指向之前的提交。
这也是git branch --contains 背后的概念。因为提交B 可从提交M 访问,从M 开始并向后退一步,git branch --contains <em>specifier-for- 将打印名称Bbranch。该名称指向M 和M 到达B。更一般地说,Git 反复询问和回答问题:
X 是提交 Y 的祖先吗?使用git branch --contains <em>anything that finds some commit</em>,Git 会针对每个分支名称询问有关每个分支提示提交的问题:
target=$(git rev-parse $argument) || exit 1
git for-each-ref --format='%(refname)' refs/heads |
while read fullbranchname; do
branchtip=$(git rev-parse $fullbranchname)
if git merge-base --is-ancestor $branchtip $target; then
echo "branch ${fullbranchname#refs/heads/} contains $argument"
fi
done
如果$argument 是类似branch 的分支名称,则将其转换为哈希ID 的操作选择提交M 的哈希ID。然后将refs/heads/feature 转换为哈希ID 的操作选择B 的哈希ID,git merge-base --is-ancestor 回答问题是提交B 是提交M 的祖先(它是)。2
您要问的有点复杂:对于每个分支名称,您不仅要确定 该分支的提示提交是否在某些选定分支的提示提交之前(正如git branch --contains 所做的那样),而且对于每个这样的分支名称,哪些是more-ancestor-y,哪些是less-ancestor-y。这个问题可以回答,但仅在某些情况下。
例如,考虑这个图表:
...--o--o--o--o---M1--M2-o <-- master
\ \ / /
\ o--o / <-- feature1
\ /
o--o--o--o <-- feature2
我认为您希望 feature2 被提及为“在”feature1 之后,因为引入 feature2 尖端的合并提交出现在引入 feature1 尖端的合并提交之后。3 但合并并不总是将 两个 提交放在一起。4 考虑:
o--o--o <-- feature1
/ \
...--o--o--o--o---M1--o <-- master
\ /
o--o--o <-- feature2
现在没有明显的顺序:feature1 和 feature2 都是通过一次提交同时引入的。
(当然,如果我们删除两个feature 名称中的一个,就可以解决问题。如果我们删除两个 名称,问题就更解决了!?)
1您可以删除提交,方法是删除对提交的所有访问。 Git 从已知的名称开始——分支和标签名称,以及所有其他形式的引用——并通过图表向后工作。如果这种向后工作达到提交,则必须保留该提交。如果不是,则提交有资格被删除。除了显而易见的名称之外,还有其他名称通常可以使提交至少多活 30 天。
2我们可以消除git rev-parse 调用,因为git merge-base 接受分支名称代替哈希ID。当给定一个名字时,它只会找到提交哈希,就像git rev-parse 一样。我放它们主要是为了说明。
3您必须编写自己的代码来生成这种排序。请注意,这并不像比较各个分支的提示提交那么简单;我们需要知道哪些合并(如果有)引入了这些提示,并比较合并。我们还必须考虑通过将它们的尖端嵌入到非合并 y 子图中而形成的分支:
..... <-- branch3
.
...--o--o--o--o <-- branch1
.
........ <-- branch2
在这里,我想我们要认领branch2 < branch3。
4即使在一次一对合并的情况下,您也可以反复将一些提交与一些主线合并,这会给问题带来麻烦(有 /em> 可能的排序,但您必须选择一些图遍历算法来在这里选择总顺序)。从本质上讲,请记住,当您使用 DAG 时,您正在处理的是 poset。
【讨论】: