在下图中,从r2(HEAD
)和r1(75ec2933~1
)都可以到达最后两个提交。
> git log --oneline --graph develop topic 75ec2933~1..HEAD
* 91cc860a (HEAD -> topic)
* 1048e4d1
* 1c28716e
| * f4a483cc (develop)
| * b7cb53e6
| |\
| |/
|/|
* | c7a197bd
* | 3935a1a7
| * ad27a1fc
| |\
| |/
|/|
* | 75ec2933 Merge branch 'develop'
| * 5e55f38f
|/
* 2effd96f <--------------- 75ec2933~1 is r1
* ae6c987e
* ecc2b546
我希望最后两个提交不会成为输出的一部分,因为the git-log
documentation说我们可以使用修订范围来“仅显示指定修订范围内的提交”。此外,修订范围文档中说about the r1..r2
notation:
...您可以请求从r2可以到达的提交,但不包括^ r1 r2从r1可以到达的提交,可以将其写为r1..r2。
所以我的问题是为什么我们可以看到最近的两次提交,这些提交似乎可以从r1
到达。
事实证明,75ec2933~1
不是2effd96f
,而是887b3cfa
。上图隐藏了该图,这导致我对r2
感到困惑。
> git log ecc2b546..3935a1a7 --oneline --graph
* 3935a1a7
* 75ec2933 Merge branch 'develop'
|\
| * 2effd96f
| * ae6c987e
* 887b3cfa
* 62e6be09
答案 0 :(得分:2)
我不得不在这里猜测一下(更新:已确认),但我认为我们有这种情况:
75ec2933
是合并提交,即具有两个父母。887b3cfa
)。2effd96f
。在这种情况下,表达式75ec2933~1..HEAD
排除父#1,但不排除父#2。您可以通过运行以下内容找出答案:
git rev-parse 75ec2933^@
(请注意,插入符号或帽子@
后的^
后缀)。对于产生的git log
输出,有相当长的解释。但是,为了演示它,我将改用Git仓库作为Git本身,因为这是我很方便的。
当我在Git的Git存储库中对另一个合并提交执行此操作时,会发生以下情况:
$ git rev-parse a562a11983^@
7fa92ba40abbe4236226e7d91e664bbeab8c43f2
ad6f028f067673cadadbc2219fcb0bb864300a6c
此处提交a562a11983
与父母7fa92ba40a
和ad6f028f06
合并。
如果我在Git的Git存储库上运行git log --decorate --oneline --graph
,则允许git log
从提交b5101f9297
开始(旧的master
提示-我尚未更新自己的Git现在已经有数周的时间存储在Git的存储库中),结果从以下开始:
* b5101f9297 (HEAD -> master) Fourth batch after 2.20
* a562a11983 Merge branch 'it/log-format-source'
|\
| * ad6f028f06 log: add %S option (like --source) to log --format
* | 7fa92ba40a Merge branch 'js/add-e-clear-patch-before-stating'
|\ \
| * | fa6f225e01 add --edit: truncate the patch file
* | | 371820d5f1 Merge branch 'bc/tree-walk-oid'
|\ \ \
| * | | 974e4a85e3 cache: make oidcpy always copy GIT_MAX_RAWSZ bytes
| * | | ea82b2a085 tree-walk: store object_id in a separate member
| * | | f55ac4311a match-trees: use hashcpy to splice trees
| * | | 36775ab524 match-trees: compute buffer offset correctly when splicing
| * | | 0a3faa45b1 tree-walk: copy object ID before use
| | |/
| |/|
* | | a6e3839976 Merge branch 'jt/upload-pack-deepen-relative-proto-v2'
使用git log --decorate --oneline --graph a562a11983^1..HEAD
将其修剪为:
* b5101f9297 (HEAD -> master) Fourth batch after 2.20
* a562a11983 Merge branch 'it/log-format-source'
* ad6f028f06 log: add %S option (like --source) to log --format
请注意,此图形形状看起来要简单得多!我已经删除了提交a562a11983
,但没有删除ad6f028f06
,因此看起来提交a562a11983
有一个父项ad6f028f06
,尽管实际上有两个。实际上, git log --graph
欺骗了我们。
在深入研究git log
本身的具体细节之前,还需要注意一些其他事项。首先,gitrevisions notation中的语法r1..r2
等效于r2 ^r1
。实际上,如果我们使用git rev-parse
来扩展 语法,那就是我们所看到的:
git rev-parse a562a11983^1..HEAD
b5101f929789889c2e536d915698f58d5c5c6b7a
^7fa92ba40abbe4236226e7d91e664bbeab8c43f2
HEAD
是以b5101
开头的提交哈希,而a562a11983^1
(后缀^
和数字1)是以7fa92b...
开头的提交。 '在此处使用插入符号^
作为后缀,而不是作为前缀; 插入符号作为前缀表示 ,即排除修订,但插入符号作为后缀引入了许多其他gitrevisions指定符之一,例如@
,{commit}
,当然还有特定父级的数字选择。 / p>
另一个事实是,每个提交记录的零个或更多父哈希ID。大多数提交具有完全一个父ID。您在存储库中进行的第一个提交没有父项,原因很简单,因为它不能有任何父项:新提交的父级ID必须是现有的有效提交哈希ID。没有父母的提交称为 root commit 。某些提交(通常由git merge
进行的提交有两个父级,您可以进行具有三个或更多个父级的多臂章鱼合并)。根据定义,具有两个或多个父哈希ID的任何提交都是合并提交。
由于大多数提交都有一个父提交,因此我们通常从这样的提交链的末尾开始,通常以诸如master
之类的分支标签标记,然后我们可以一次向后进行一次提交:< / p>
... <-F <-G <-H <-- master (HEAD)
在这里,存储在分支名称master
中的哈希ID用大写字母H
表示。我们说名称master
指向哈希ID为H
的提交。提交H
本身存储其父提交G
的哈希ID,该哈希ID存储提交F
的哈希ID,依此类推。因此,通过从H
开始并向后退回到G
,然后再回到F
,依此类推,Git可以向我们展示历史记录-可以从到达的提交- em>名称master
。
最后一项是git log
实际上采用了许多起点(我们可能希望将它们称为终点,但是Git可以逆向工作)。每个指定修订的参数(而不是那些由于用前缀^
否定而消除修订的参数)都提供了这样的起点。如果您没有提供任何起点,则git log
将使用HEAD
作为起点。
git log
如何浏览历史记录,然后显示图形如果我们做有一条简单的线性链,例如:
...--F--G--H <-- master (HEAD)
然后,如果我们想模仿git log
,我们的工作就很容易。我们从提交H
开始并显示它。现在我们已经完成了H
的工作,因此我们向后退了一步,回到了其父级G
。我们显示G
,然后回到F
。我们重复此过程,直到达到没有父级且允许我们停止的根提交为止,或者直到用户退出git log
。
但是假设我们有一个包含合并提交的图形:
I--J
/ \
...--H M--N <-- master (HEAD)
\ /
K--L
我们将首先显示提交N
,然后走到M
并显示它。 1 然后,我们走到...等等,走吧到J
还是L
?
git log
要做的是,保留尚未显示的提交的优先级队列,而还要遍历提交图一次提交一次。因此,当您在没有其他参数的情况下运行git log
或以HEAD
或master
作为参数运行时,git log
会将提交N
放入队列。
如果队列中只有一个提交,则工作很容易:将一个提交从队列中取出,显示出来,然后将其父级放入队列,如果在此之前{{ {1}}(通常是这种情况)。当队列中有个以上提交时,git log
将从队列的 front 中获取一个,即优先级最高的一个。>
因此,如果您运行git log
,则Git所做的工作会将所有三个起始点放入优先级队列。由于您的实际命令是:
git log <start-point-1> <start-point-2> <start-point-3>
我们有三个起点,分别是git log --oneline --graph develop topic 75ec2933~1..HEAD
(develop
),f4a483cc
和topic
(HEAD
是对某些哈希ID的否定引用) 。事实证明,75ec2933~1
和HEAD
都命名为提交topic
,因此队列中只有两个提交而结束。
91cc860a
选项会稍微修改优先级队列。默认设置是具有最高 date 的提交(即,距离将来最远或过去最少的提交)位于队列的最前面。对于--graph
或--graph
,这条规则已经到位,但是增加了一条附加规则:在显示了将要显示的所有子项之前,无法显示任何父提交。在这种情况下,由于--topo-order
和91cc860a
没有父/子关系,因此额外的例外此时不起作用。
因此f4a483cc
从这两个日期较晚的日期开始,也就是git log
和91cc860a
。 Git使用单个HEAD
打印此提交,并找到其父级topic
,该父级进入队列。 *
也比1048e4d1
更新,因此Git接下来显示它。它是上一次提交的直接父项,因此现在该展示该提交了。这样会持续一段时间,以便我们看到:
1048e4d1
f4a483cc
具有父元素* 91cc860a (HEAD -> topic)
* 1048e4d1
* 1c28716e
,而1c28716e
是c7a197bd
的祖先,因此无论日期如何,都不得显示。 Git现在开始显示c7a197bd
,这是一个普通的提交:
f4a483cc
f4a483cc
的父级是| * f4a483cc (develop)
,因此f4a483cc
进入了队列。该提交具有b7cb53e6
作为祖先,因此Git接下来显示b7cb53e6
:
c7a197bd
...和b7cb53e6
本身是一个合并,将其父项| * b7cb53e6
和b7cb53e6
放入队列。但是c7a197bd
已经在队列中,因此什么也没发生。
现在ad27a1fc
位于队列的 front ,因此c7a197bd
显示了它。它是c7a197bd
的第一个也是唯一的父级,也是git log
的第二个父级,因此1c28716e
以这种有点时髦的方式显示它:
b7cb53e6
向右延伸的下肢显示出该第二亲本。直脚将最终连接到git log --graph
的第一个父级。
相同的模式持续了一段时间,但随后我们遇到了不幸的情况:
| |\
| |/
|/|
* | c7a197bd
这时,Git显示了提交b7cb53e6
(有两个父级,* | 3935a1a7
| * ad27a1fc
| |\
| |/
|/|
* | 75ec2933 Merge branch 'develop'
| * 5e55f38f
|/
* 2effd96f <--------------- ???
是父级#1,75ec2933
是父级#2)。 Git 本来应该将887b3cfa
放入队列,但我们告诉它不要:2effd96f
的意思是887b3cfa
,这意味着不要显示{{ 1}} ,将其排除在队列之外。因此,在显示了^75ec2933~1
之后,该队列包含了提交哈希ID ^887b3cfa
和887b3cfa
。 Git显示了75ec2933
,这使其可以继续前进到5e55f38f
。当2effd96f
显示一个时,甚至没有看到第二个截止父对象,因此它错误地绘制了该图,就好像该父对象不存在一样。< sup> 2
1 值得一提的是:当5e55f38f
显示普通提交时,如果2effd96f
有效,它会将提交与其(单)父对象进行对比,以显示作为更改,它实际上是快照。但是,当它遇到合并提交时,它不知道要使用哪个父对象作为差异,因此根本不用理会差异!您可以通过其他git log --graph
选项强制其显示一个或多个差异。
2 公平地说,此时的内存中表示可能没有具有第二个父对象。 git log
代码包含一些用于简化历史记录的“父重写”代码,这也可能在此处触发。
我通常会告诉人们,如果-p
显示出奇怪的结果,他们应该添加git log
以使其既绘制图形(还是粗略的ASCII近似值)并在行走时遵循图形拓扑通过提交,以便显示通常很关键的父子关系。不幸的是,当您使用否定符将图形的某些部分剪裁掉时,这可能会使图形绘制代码陷入绘制谎言中。可能应该将图形绘制代码显示为真实的多亲情况,因此被迫绘制最后一部分,如下所示:
git log
但事实并非如此,因此除非/直到有人可以将其添加到Git中,否则我们只需要提防此类情况。