首页 > 解决方案 > 从提交 ID 创建 git 分支后,一些 git 提交丢失

问题描述

我正在从提交 id 创建一个 git 分支,我的期望是它应该在创建新分支时在指定的提交 id 之后显示提交 id。但不知何故,新创建的 git 分支中缺少一些 git 提交。

我有一个有多个提交 id 的主分支:

主分支提交 ID

我使用git checkout -b testBranch 3331a4b命令从主分支创建了一个新分支

但是当在这个分支上触发git log --oneline命令时,我可以看到一些提交 id 丢失了。请点击以下链接查看提交 ID

testBranch 提交 ID: testBranch 提交 ID

缺少主分支的提交 ID: 缺少主分支的提交 ID

标签: gitgithubgit-branch

解决方案


这里的问题是,虽然git log --oneline显示提交的线性列表,但提交实际上并不是线性的。考虑到一些明显线性的提交序列,您期望这一点:

010 final
009 earlier
008 earlier-still
007 ...
...
001 first

从 010 开始倒数会给你 10 次提交,而从 009 开始倒数会给你 9 次提交。

如果提交是线性的,那将是正确的,但事实并非如此。

当然,实际的数字不是连续的,也不是十进制的——它们是哈希 ID,例如(对于简单地以 开头3331a4b的更长的东西来说,它已经很短了)。所以你已经知道你不能只使用数字作为一个简单的索引。让我们用单个大写字母...替换序列号(001 到 010)或哈希 ID(等等) ,并实际绘制在一个包含十次提交的小型存储库中进行的提交,最后一次提交是合并提交: 3331a4b3331a4bA BH

A  <-B  <-C  <-D  <-----H
           \           /
            E  <-F  <-G

这些箭头——来自E指向C和来自H指向G的箭头没有箭头提示(因为在文本中绘制太难了),但这些连接器也是箭头——允许 Git 从最后一次提交向后工作到第一的。

事实上,每个 Git 提交都包含一组父提交哈希 ID。我们说提交指向它的父节点(如上图所示)。大多数提交只有一个父级,这些提交形成一个简单的、向后的、线性链,就像从Cback 到A. 在这里,一切都表现得很好:当您从 开始时C,只有一条路径可以返回A.

至少有一个提交,第一个提交,没有父哈希,因为它是第一个提交,并且没有更早的提交指向它。这是动作停止的地方。请注意,该操作从末尾开始并向后进行。

一些提交,如H上面示例中的提交,有两个父级。在这里,事情变得很棘手,因为 Git 可以从H GD. (您可能已经看到了它的发展方向。)该git log命令使用一种特定的方法来线性化它的向后行走,我们稍后会谈到。

一个类似于或实际上指向一个提交的分支名称——<em>一个特定的提交——例如 commit ,所以我们可以更简单地绘制这些东西,像这样,然后在右边添加分支名称,最后:masterdevelopH

A--B--C--D------H   <-- master
       \       /
        E--F--G   <-- develop

知道提交之间的内部链接是向后的箭头。(为了不需要更改提交,箭头本身嵌入到级中,指向它们的父级提交——提交中的任何内容都不能移动或更改;所有提交的内容都是永久固定的。子级孩子出生的时候就知道自己的父母是谁,但是直到孩子出生,父母才知道自己的孩子会是谁,所以父母不能指点孩子。)

这里棘手的部分是名称,如masteror develop确实会移动:名称通过命令master到达这里,可能通过 运行,或者可能由某人直接运行,或者通过 GitHub“合并拉取请求”按钮,或类似的。在早些时候的某个时候,绘图看起来像这样:git mergegit pull

A--B--C--D   <-- master
       \
        E--F--G   <-- develop

CommitH尚不存在,并且从开始master并向后工作,git log将枚举 commits D, then C, then B, then A; 从开始develop并向后工作,git log将枚举提交G,然后F,然后E,然后C,然后B,然后A

此时,有人跑了git checkout master; git merge develop,或者做了类似的事情。这创建了新的 commit H,指向Gand D,并为我们提供了最新的绘图。

现在,假设您将一个的分支名称附加到 commitFE. 如果您一直在查看git log基于 的输出master,并且一直假设提交D在提交之前E,您可能希望看到从For开始的提交E,向后工作并包括D. 但是,一旦我们绘制了提交图,并在For上附加了一个标签E,您就会发现我们不应该期望提交D出现:

A--B--C--D------H   <-- master
       \       /
        E--F--G   <-- develop
            .
             .....<-- testBranch

D可以从H( , 目前) 到达的事实master并不意味着D可以从 到达testBranch

在更复杂的图表中,从某个分支尖端开始并向后工作的提交集可能更难看到:图表变得复杂,难以查看或可视化。尽管如此,使用git log --graph(有或没有--oneline)在这里可以有所帮助。

如何git log线性化非线性图

由于git log必须一次显示一个提交 - 每行一个--oneline,或者使用多个输出行而不使用它 - 它需要一个待处理提交队列。这个队列通常从你命名的一个提交开始:

$ git log <hash>

或者:

$ git log <branchname>

或者没有参数,当前的 ( HEAD) 提交。Git 会将提交从队列中取出,显示出来,然后将其父项放入队列中。如果刚刚显示的提交是合并提交,具有两个(或更多)父级,则队列现在变得更深。

然后,Git 获取队列前面的任何提交并显示该提交。该提交有一些父母;如果父级尚未显示且不在队列中,Git 会将它们放入队列中。

因此,提交的显示顺序取决于它们在队列中的位置。当队列为空(通常是这样)并且只有一个父节点时,该父节点将成为队列中的单个条目,并立即显示,将其从队列中删除,从而使队列再次为空。这就是一个简单的线性链如何显示为一个简单的线性链:我们可能将提交G放入队列,然后将其取出并显示并放入F队列,然后将其取出并显示并放入E队列, 等等。

如果您从git log多个提交开始:

$ git log master develop

(假设名称masterdevelop指向两个不同的提交——可能有两个分支名称指向同一个提交),队列开始时会有多个提交,所以再一次,队列中的提交顺序将事情。Git 将首先显示队列前提交,无论是master命名的还是develop命名的。显示该提交后,Git 将在适当的情况下将其父级插入队列中,然后才有机会(取决于另一个提交现在在队列中的位置)显示您在命令行中指定的另一个提交。

队列插入顺序的默认值是提交时间戳,较新的(较晚的时间戳)提交进入队列的前面。--date-order您可以使用、--author-date-order--topo-order和来在一定程度上控制此顺序--reverse。使用--graph武力--topo-order。有关详细信息,请参阅git log文档


推荐阅读