当你运行时,git log
你告诉它从哪个提交开始。如果你不告诉它,它会假设当前分支的当前提交。它会找到它可以从该起点找到的其余提交。您已8d6995e4
在您的存储库中提交,但它不在您当前的分支上。
您可能会git log --all
告诉它从所有记录的起点(分支名称、标签名称、远程跟踪名称等)开始,并且您可能想要从狗那里获取帮助,或者按照OznOg 的建议,使用视觉查看器。另请参阅漂亮的 git 分支图。
要了解这里发生了什么,您需要了解提交——这是 Git存在的理由——以及 Git 如何找到它们,通常从分支名称开始。这进入了图中可达性的概念,在Think Like (a) Git中以更长、更彻底的方式进行了解释。
每个提交都有自己唯一的哈希 ID。这就是你在这里看到的那个又大又丑的字符串8d6995e4
(尽管实际上它要长得多——40 个字符显然是随机的垃圾,实际上根本不是随机的)。该哈希 ID 是提交的“真实名称”:它是 Git 在“所有提交过”的数据库中查找提交的方式(人们所做的主要事情是添加更多提交)。
每个提交还包含一个父提交哈希 ID。也就是说,某个父提交的子提交会记住父提交的哈希值。父母不记得他们的孩子,因为一旦做出承诺,里面的任何东西都不会改变。提交的这种冻结git log
对许多其他 Git 命令来说无关紧要,但确实很重要。
上面有点夸张,因为至少有一个提交——某人在一个新的空存储库中所做的第一个提交——没有父级。的父这个承诺就是承诺已来此承诺。第一次提交没有在它之前的提交。并且,合并提交有不止一个父级,这就是它们合并提交的原因。
这一切意味着提交形成了一个向后看的链。使用的正是这个反向链git log
。这是一个只有三个提交的存储库的简单绘图,其中我只使用一个大写字母而不是丑陋的 40 个字符的哈希 ID。显然,我们会很快用完——存储库将在 26 次提交时填满——所以这不是 Git 的做法,但它确实很好地说明了问题。我们的第一个提交是A
,它没有父级。然后我们 make B
whichA
作为它的父级,最后,我们 make C
whichB
作为它的父级。
A <-B <-C
因为提交中的任何内容都不会改变,所以我们可以将从子级到父级反向的连接绘制为双向的线,只要我们记住Git必须向后移动。这对我在 StackOverflow 帖子中更方便,因为我有时也需要做对角线:
A--B--C
\
D
我没有适合这里所有人的方便的文本箭头。
现在,为了git log
工作,你必须给它一个开始提交——一个哈希 ID。事实上,为了让 Git 中的很多东西都能工作,你必须给它们一个哈希 ID。但是真正的哈希 ID 又大又丑 ( 8d6995e4
) 并且显然是随机的。它们绝对不是一个合理的顺序,人类没有办法记住它们。所以我们要做的是选择一个分支名称,例如master
. 我们让 Git 将最后一次提交的哈希 ID 推送到分支名称中,这意味着我们的绘图现在看起来像这样:
A--B--C <-- master
该名称让 Git 找到最后一次提交,在本例中为C
。从那里,git log
走倒退到B
,然后回A
,然后因为A
没有父,混帐终于可以停下来。
为了添加新的提交,Git 会打包你提交的任何内容——一个新的快照——并添加元数据,比如你的姓名和电子邮件地址、当前时间和你的日志消息。Git 会自动将当前提交的哈希 ID 添加到元数据中。Git 将所有这些都写入对象数据库,写入操作会为这个新提交分配唯一的哈希 ID。当然,那是另一个丑陋的大数字,但我们将使用字母D
:
A--B--C <-- master
\
D
为了记住这D
是我们最后一次提交,Git 现在更新我们的名字master
:
A--B--C
\
D <-- master
所以现在git log
将开始于D
,然后显示C
,然后B
,然后A
。
重要的是提交。分支名称可以帮助我们找到它们。但是当我们有多个分支名称时会发生什么?在我们进行了更多提交之后,让我们看一个稍微复杂的图:
...--C--D <-- master
\
E--F <-- develop
我们上面说过,当我们进行新的提交时,Git 会移动分支名称。但是 Git 如何知道要移动哪个分支名称呢?如果我们有:
...--C--D <-- develop, master
——我们可能在某个时候做了——当我们进行 commit 时E
,Git 怎么知道它应该移动的是开发,而不是master
?
答案是我们git checkout
用来将特殊名称附加HEAD
到一个分支上。如果我们选择develop
,我们得到:
...--C--D <-- develop (HEAD), master
然后当我们添加 commit 时E
,Git 移动develop
:
...--C--D <-- master
\
E <-- develop (HEAD)
HEAD
即使分支名称本身移动,名称仍会附加到分支名称。(对于那些好奇的人来说,它的实际实现是.git
named中有一个文件HEAD
,HEAD
它只包含分支名称本身。因此,只有当您选择要使用的新分支时,才会更改其内容。分支的内容名称——最新提交的哈希 IDmaster
和develop
——存储在其他地方,在一个或多个不同的地方。)
顺便说一下,请注意,在这里的图中,提交A-B-C-D
是在两个分支上。提交E-F
仅在develop
. 但是因为我们可以移动分支名称,我们可以通过移动master
到指向来改变它F
。这将使所有六个提交都在两个分支上。我们不必更改提交来执行此操作,这是必要的,因为我们不能。:-)
图表可能会变得更加复杂。这里没有展示一个非常复杂的(比如你可能实际拥有的那个),而是一个更简单的展示了当我们提交合并时会发生什么。这是之前的图片:
I--J <-- release (HEAD)
/
...--G--H
\
K--L <-- develop
和之后的一个:
I--J
/ \
...--G--H M <-- release (HEAD)
\ /
K--L <-- develop
新的合并提交M
仅在release
. 它有两个父母,J
和L
。CommitJ
仅在发布时生效,并且它有一个 parent I
。CommitL
在两个分支上都有,并且有一个 parent K
。双方I
并K
引回犯H
,这是两个分支,并引回G
,这是对双方,等等。
git log
进来的地方如果你在git log
没有开始提交的情况下运行,Git 会查看HEAD
你的当前提交。您当前的提交是您的分支名称指向的提交。你在评论中提到你在你的release
分支上并且它指向一个合并提交。所以你的图片可能看起来像我们刚刚画的。
该git log
命令将从提交开始M
并向您显示。然后添加所有的M
父母到它需要显示提交的名单。现在必须努力你都显示J
,并L
在同一时间。它不能,所以它选择这两个之一来显示。然后它将它显示的任何一个的父项添加到要显示的内容列表中。
如果它只是显示J
,git log
现在有两个提交同时显示给你,即L
和I
。它不能同时显示两个,所以它选择一个。然后它将那个人的父母添加到它的列表中,依此类推。
git log
默认情况下,该命令将混合来自合并两侧的提交。特别是,它根据时间戳一次显示一个,以便您首先看到较新的。如果J
比L
您J
之前看到的要新L
,但如果L
比J
您L
首先看到的要新。这同样适用于I
和K
(VS都L
和J
)。但是git log
确实尊重图形的形状,因为无论它做什么,它都不会显示,H
直到它完成显示I-J
和K-L
。
最终,它将能够显示H
,并且当它显示时,其他提交的列表将是空的。它将显示H
,将H
的父级添加G
到要显示的列表(现在只有一个条目),然后显示G
并添加G
的父级,依此类推。
但是git log
不会显示它无法通过开始M
和向后工作来完成的提交!
I--J
/ \
...--G--H M <-- release (HEAD)
\ /
K--L <-- develop
\
N--O--P <-- another-name
假设还有一些其他名字记住了一个哈希 ID P
。这可能不是分支名称:可能是类似origin/develop
或的名称origin/master
,您的 Git 使用它来记住您的 Git 在其他某个 Git 中看到的分支名称。
运行git log
不会显示 commit P
。没有显示P
它也不会移回O
。Git 从字面上看不能从L
到N
。它只能从N
到L
,倒退。您需要选择在您关心的提交处或之后开始的起始提交哈希,以查看您关心的提交。
请注意,N-O-P
此处的提交不是 "on" release
。我们使用“on”这个词来表示“提交可从提示到达”。的提示release
是提交M
,N
从M
.
请注意,M
不能通过从N
任一方式向后进行提交!两个提交可以有一个共同的祖先,而它们本身没有父/子或祖父/孙子关系。他们有共同的祖先这一事实可能只会使他们成为兄弟姐妹或堂兄弟。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句