【问题标题】:Visualize git branch dependency可视化 git 分支依赖
【发布时间】:2019-06-04 23:46:11
【问题描述】:

是否有工具可以显示特定分支已合并到哪些分支?例如,如果“A”已合并为“B”和“C”,但没有合并为“D”,如何输出“B”和“C”?我只需要分支名称。

【问题讨论】:

    标签: git branch git-log


    【解决方案1】:

    如果您指的是图形工具,gitk 似乎是一个不错的起点。

    或者,您可能更喜欢对某些需求非常有用的 CLI,试一试:

    git log --oneline --decorate --simplify-by-decoration --graph --all
    

    更具体地解决“如何知道哪些分支合并到分支 A?”的问题

    git branch --contains A
    

    【讨论】:

    • 谢谢,虽然我只想知道关于分支“后代”的非常具体的信息,就像我描述的那样。如果父分支是A,我想快速知道这个分支合并到了哪些分支,而不必扫描git树。你认为这可以通过命令行实现吗?
    • 天哪,这太容易了,谢谢!当我引起你的注意时,假设 A 被合并到 B 中,然后 B 被合并到 C 中,我能否得到与 git branch --contains A 类似的输出,但表明 A 先被合并到 B 中,然后 B 被合并到 C 中? (目前会分别显示B和C)
    • @Ivan 那么也许是时候考虑使用工具了。 gitk 实际上非常有用,如果您可以通过相当过时的界面。您是否尝试过并出于特定原因排除了它?
    • 不是 gitk,尝试了其他几个客户端,但还没有找到一个可以让我轻松做到这一点的客户端(并且在尝试 gitk 时它也是不够的)。再次感谢您的帮助。
    【解决方案2】:

    重要的是要意识到分支名称实际上并没有任何意义。

    分支名称只是一个标签或指针,指向一个提交。将它们视为黄色便签之一,甚至是sticky arrows,您可以将其粘贴到提交上。重要的是提交。提交的标识是它的哈希 ID。提交是永久的1 且不可更改,并且每个提交记录其直接父级(大多数提交)或父级(合并提交为 2 个或更多)的哈希 ID。这就是生成提交图的原因,它——除了你总是可以添加到它之外,它与提交本身一样永久且不可更改。

    是图形本身决定了存储库的实际“分支”:

    (older commits towards the left, newer ones towards the right)
    
              o--A
             /
    ...--o--o
             \
              o--o--B
    

    图表的这个特定部分显示了两个分支,以两个提示提交AB 结束。在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
    

    需要两个标签——两个便笺箭头——来记住AB,或者同时记住CB,但是现在我们有了M,我们可以处理其中一个这些标签,只保留一个指向 M 本身的便签箭头:

              o--A--C
             /       \
    ...--o--o         M   <-- branch
             \       /
              o--o--B
    

    所以这就是分支名称的作用:它是一个指针,指向一个提交,Git会认为这是一些向后的tip提交- 看起来的提交链。随着 Git 向后工作,从这个提示提交开始,Git 将遍历合并的 所有 分支,以找到所有可以通过向后工作到达的提交。由于M 有两个父母CB,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-B 将打印名称branch。该名称指向MM 到达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
    

    现在没有明显的顺序:feature1feature2 都是通过一次提交同时引入的。

    (当然,如果我们删除两个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 &lt; branch3

    4即使在一次一对合并的情况下,您也可以反复将一些提交与一些主线合并,这会给问题带来麻烦(有 /em> 可能的排序,但您必须选择一些图遍历算法来在这里选择总顺序)。从本质上讲,请记住,当您使用 DAG 时,您正在处理的是 poset

    【讨论】:

    • 我知道这方面的基础知识,但是您的扩展(而且很棒)的解释帮助我更好地理解了,尤其是关于分支只是指针的观点。将不得不重新考虑我的工作流程。谢谢!
    猜你喜欢
    • 2011-09-23
    • 2019-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-12
    • 2016-06-12
    • 1970-01-01
    • 2014-02-02
    相关资源
    最近更新 更多