使用Git时,如何根据?创建新分支的哪个分支?

时间:2014-04-18 06:48:53

标签: git

如果我做了

,请说
git checkout -b bar foo

git checkout foo
git branch bar

两者都应该在分支bar上创建一个分支foo。但是,让我们说第二天,如果我想验证bar是否真的是基于foo创建的,那么在git中没有简单的方法可以做到这一点,或者在那里?

使用应用程序SourceTree,似乎可以且很容易判断,如果barfoo显示在同一行上,则表示它们在该阶段是相同的,如果我在bar中提交任何内容,它会显示bar是"扩展"来自树形图中的foo,因此我可以轻松地告诉bar基于foo。我在SO中发现的其他一些问题似乎表明没有简单的说法,但我不明白为什么。至少在有可能告诉时,不应该有一个告诉它的命令,例如git branch --show-base-on之类的东西,或者类似的东西,当不可能告诉时,那么显示"不能告诉因为[某种原因]"?

3 个答案:

答案 0 :(得分:3)

git checkout -b bar foo的最后一个参数实际上只是一个提交ID。您使用了分支名称foo,但是git将其解析为commit-ID。结果是git使用相同的commit-ID创建新的分支标签bar

我们假设你有以下提交图:

A <- B <- C <- D
            \
              E

(ASCII没有正确的向上和向左箭头,所以假设\的箭头指向C,即提交E的父提交是C)。每个字母代表一个与提交相对应的唯一SHA-1 ID。提交D将其(单个)父项作为提交C,提交E也是如此,因此分支在提交C处分叉。

现在让我们粘贴一些分支标签。名为bra的分支包含提交A的提交ID,因此它指向A。名为brb的分支包含提交B的提交ID,因此它指向B。分支brc指向C,依此类推。 (我无法在ASCII中正确地绘制所有这些,所以我们必须简单地想象它们。)

让我们再建一个分支,比如master,它也指向提交D

为了超级具体,让我们说D的SHA-1是5f95c9f850b19b368c43ae399cc831b17a26a5ac

现在,如果我这样做:

git checkout -b new 5f95c9f850b19b368c43ae399cc831b17a26a5ac

你认为分支new基于哪个分支?新分支标签new指向提交D,就像现有分支标签brdmaster一样。 new来自brdmaster,还是......?

你可能对此有答案,但是git没有。 git中的分支标签是短暂的:它们可以随时扫除和/或重新创建或重命名。 (如果我删除了标签master,只有brd指向D,那么您可以说分支new基于分支brd。但是如果我随后将brd重命名为bird该怎么办?)

在git中唯一真正永久的东西是SHA-1值,它完全取决于底层对象的内容。创建完全相同的对象内容,您将获得完全相同的SHA-1哈希。在任何对象中的任何位置更改一个位,您将获得一个新的,不同的SHA-1。

由于提交记录了它们的父提交(在合并的情况下是多个父项),通过从一些引用开始并向后工作到无父提交(根提交)构建的图是永久的(假设起点保留在存储库中,即永远不会被垃圾收集),但标签不是。 (只有当没有外部参考分支,标记等指向它们没有其他已经保留的存储库对象 1 指向它们时,提交是垃圾收集,所以对像E之类的图形提示的单一引用足以保留整个提交链。但是如果删除该引用,则链变得易受攻击。)


1 这些&#34;其他存储库对象&#34;必须是其他提交或带注释的标记对象,因为它们是唯一应该包含提交的SHA-1的两个。带注释的标记对象本身应该由相应的轻量级标记指向引用,或者可能是另一个带注释的标记。


顺便提一下,git branch可以帮助您,如果您想要提出不同的问题。让我们假设您有一个看起来更像这样的提交图:

...-o-o-o-o-o-A   <-- master
     \     /
      o-o-B       <-- feature1
           \
            C     <-- feature2

其中每个o节点代表一个&#34;无趣的&#34; (并且未标记)提交。此处标记的分支提示提交(ABC)是masterfeature1和{{1}的分支提示}。 (可能feature2是通过feature2分支创建的,而feature1是通过分支feature1创建的,但通常这也不是特别有趣。)

如果您现在要在分支master上工作,您经常想知道的是:&#34;合并哪些分支,哪些分支未合并?&#34; master命令可以回答这个问题:

git branch

git branch --merged master

两个命令都要求git branch --no-merged master git branch标识的提交开始(即提交master)和&#34;向后工作&#34;沿着提交图。

使用A时,它应该打印分支的名称,当从--merged向后工作时,会到达这些分支所标识的提交。我们从master开始,然后回到最右边的A。这是一个合并提交(有两个父母),所以我们然后沿着父母一起退一步,找到下一个o提交并提交o。分支名称B指向提交B。从feature1到达 ,因此master会打印git branch。这是一个&#34;合并到&#34;分支feature1

使用master--no-merged命令应该打印那些提交的分支。和以前一样,我们从git branch开始向后工作,找到最合适的A。我们向父母双方倒退,找到另一个oo;继续回来我们又发现了两个B,另外两个o,然后重新加入最左边的o并返回o部分。在任何时候我们都无法提交... - 所以C打印git branch:这是&#34;合并到&#34;分支feature2

还有一个伎俩master的袖子(如果它首先有任何袖子:-))。我们可以请求git branch并为其提供特定的提交ID,或任何解析为提交ID的内容。我们假设我们为其提供了提交git branch --contains的ID。

这一次,使用B--contains命令从每个分支尖端开始,然后开始向后工作。如果它可以达到我们提供的提交ID,它会打印分支名称。和以前一样,当我们从提交git branchA)开始时,我们达到提交master。当我们从提交BB)开始时,我们当然会立即达到提交feature1;当我们从提交BC)开始时,我们达到提交feature2。所以B打印所有三个分支名称。

(如果我们要问git branch --contains feature1哪些分支包含最右侧的git branch提交,则只有o会包含它。大多数顶线都是如此master提交,除了最左边的一个;分支ofeature1也包含该提交,以及feature2部分中的任何早期提交。)

答案 1 :(得分:1)

你不能这样做的原因是分支在Git中没有强大的身份。它们只是轻量级的标签&#34;这可以随着时间的推移而移动,以指示&#34;某事&#34;。

的当前位置

在未来的任何时候,有人可以根据您创建新分支的位置创建新分支,删除,重命名或移动创建新分支的原始分支,并生成您要保留的信息无法推断。

Git不需要保留有关分支指针的先前位置的任何信息,它只关心任何给定分支中实际提交的历史记录,因此如果您希望分支具有比Git本身更强的身份归属于它们那么你需要单独存储这些信息,例如在元数据分支中或者可能在Git外部。

答案 2 :(得分:0)

一种方法是检查foo HEAD表示的提交是否包含在bar分支中,但是因为foo HEAD会随着时间的推移而移动......这将随着时间的推移而无效。

请参阅“Find the parent branch of a branch”以了解查找信息(父母分支)的难度,该信息基本上是而不是由Git记忆。

作为seen here,您可以将其改为“当前分支以外的分支上最近的提交是什么,以及哪个分支是什么?”

但同样,这个结果会随着时间的推移而改变,因为分支可以被移除,重命名,重新定位。