如果我做了
,请说git checkout -b bar foo
或
git checkout foo
git branch bar
两者都应该在分支bar
上创建一个分支foo
。但是,让我们说第二天,如果我想验证bar
是否真的是基于foo
创建的,那么在git中没有简单的方法可以做到这一点,或者在那里?
使用应用程序SourceTree,似乎可以且很容易判断,如果bar
和foo
显示在同一行上,则表示它们在该阶段是相同的,如果我在bar
中提交任何内容,它会显示bar
是"扩展"来自树形图中的foo
,因此我可以轻松地告诉bar
基于foo
。我在SO中发现的其他一些问题似乎表明没有简单的说法,但我不明白为什么。至少在有可能告诉时,不应该有一个告诉它的命令,例如git branch --show-base-on
之类的东西,或者类似的东西,当不可能告诉时,那么显示"不能告诉因为[某种原因]"?
答案 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
,就像现有分支标签brd
和master
一样。 new
来自brd
,master
,还是......?
你可能对此有答案,但是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; (并且未标记)提交。此处标记的分支提示提交(A
,B
和C
)是master
,feature1
和{{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
。我们向父母双方倒退,找到另一个o
和o
;继续回来我们又发现了两个B
,另外两个o
,然后重新加入最左边的o
并返回o
部分。在任何时候我们都无法提交...
- 所以C
打印git branch
:这是不&#34;合并到&#34;分支feature2
。
还有一个伎俩master
的袖子(如果它首先有任何袖子:-))。我们可以请求git branch
并为其提供特定的提交ID,或任何解析为提交ID的内容。我们假设我们为其提供了提交git branch --contains
的ID。
这一次,使用B
,--contains
命令从每个分支尖端开始,然后开始向后工作。如果它可以达到我们提供的提交ID,它会打印分支名称。和以前一样,当我们从提交git branch
(A
)开始时,我们达到提交master
。当我们从提交B
(B
)开始时,我们当然会立即达到提交feature1
;当我们从提交B
(C
)开始时,我们达到提交feature2
。所以B
打印所有三个分支名称。
(如果我们要问git branch --contains feature1
哪些分支包含最右侧的git branch
提交,则只有o
会包含它。大多数顶线都是如此master
提交,除了最左边的一个;分支o
和feature1
也包含该提交,以及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,您可以将其改为“当前分支以外的分支上最近的提交是什么,以及哪个分支是什么?”
但同样,这个结果会随着时间的推移而改变,因为分支可以被移除,重命名,重新定位。