我们设置了一个构建流程来创建按提交日期排序的产品构建,但事实证明这并不总是正确的顺序?
最近的两个提交:
提交A
Author date: 22 hours ago (7/22/2019 16:56:46)
Commit date: 22 hours ago (7/22/2019 16:57:50)
Run Code Online (Sandbox Code Playgroud)
提交B
Author date: 22 hours ago (7/22/2019 16:57:22)
Commit date: 22 hours ago (7/22/2019 16:57:44)
Run Code Online (Sandbox Code Playgroud)
这就是它们在存储库中出现的顺序 - 提交 B 是最后一个,并且包含提交 A 的更改。然而,第一个提交的日期比第二个提交晚 6 秒。结果,构建系统以错误的顺序分配了构建号。
这是否意味着提交日期不是排序提交的可靠方法?
这里有几个不同的点需要解决。
\n\n首先,正如您所展示的,每个提交都嵌入了两个日期和时间戳。一个是作者日期,另一个是提交者日期。您可以git log使用以下命令查看这两个日期--pretty=fuller(还有其他方法,但这很简单)。
接下来,提交可以具有父/子关系,正如您在第二条评论中提到的那样中提到的那样。更确切地说:
\n\n每个提交都有一个唯一的哈希 ID。实际上,哈希 ID 是提交的“真实名称”。 git log通常会打印这些哈希 ID,因此git log --pretty=fuller以commit <hash>.
每次提交还记录一定数量的父哈希 ID。大多数提交都会存储一个父哈希 ID。这意味着每个子提交都知道其父提交是谁,即使父提交不知道其子提交是谁。换句话说,这种联系只有一种方式:从子级到父级。
后者的原因是commits\xe2\x80\x94,事实上,所有存储的Git对象\xe2\x80\x94从创建时起就被永久冻结。这是因为哈希 ID(Git 用于命名和查找每个对象)实际上只是内容的校验和存储在 Git 对象数据库中的对象如果您从数据库中取出一个对象,以任何方式修改其内容,然后将其写回,您将获得一个新的不同的校验和。原始对象保持不变。任何使用原始哈希 ID 的人都可以获得原始对象。
\n\n该git log命令的工作方式有点复杂,但开始时非常简单。类似或的分支名称仅包含一个哈希 ID。该哈希 ID 定位一个特定的提交。该特定提交是分支的尖端提交。这就是分支名称在 Git 中工作方式的定义:无论分支名称中存储什么哈希 ID,该提交都是该分支的尖端提交。Git更改存储的哈希 ID 以便更改哪个提交是分支提示:masterdeveloprelease
... <-F <-G <-H <--master\nRun Code Online (Sandbox Code Playgroud)\n\n这里,大写字母代表真实的哈希 ID。该名称 保存分支中最后一次master提交的原始哈希 ID 。Git 使用它来查找提交本身:其中的哈希 ID是 Git 在其“存储库中的所有对象”的大数据库中查找的键,而提交就是结果。Git 读取 commit并发现\ 的父级是 commit 的哈希 ID ,因此现在可以从存储库中取出提交。Commit具有其常用信息\xe2\x80\x94,包括两个日期和时间戳,以及作为其父级的 commit 的哈希 ID 。masterHHHGgit logGGGF
为了向分支添加新的提交master,Git 会写出一个新的提交\xe2\x80\x94,它会获取一些看起来随机的哈希 ID,但我们将其称为I\xe2\x80\x94,它有两个日期和-时间戳,并且以提交的哈希 IDH作为其父级。Git从 name获取哈希 ID 。新提交的写出为其分配了唯一的哈希 ID。现在提交已存在于存储库中,Git 只需使用新的哈希 ID 进行覆盖即可:HmasterImaster
... <-F <-G <-H <-I <--master\nRun Code Online (Sandbox Code Playgroud)\n\n因此,git log\xe2\x80\x94 至少对于像这样的\xe2\x80\x94 这样的简单情况来说是:
提取当前分支的哈希 ID。
显示该提交(及其日期和时间戳)。
遵循对其父级的承诺。显示该提交。
遵循对其父级的承诺。显示该提交。
重复直到用完提交。
结果是输出git log按图形顺序排列,从分支的尖端开始向后工作。 这些提交中存储的日期和时间戳并不重要。git log对于它们的重要性, 还有更复杂的情况,但让我们从这个开始。从根本上讲git log,通过将子提交与其父提交连接起来的链接形成的图表,逐个提交地向后工作。
默认情况下,git commit创建一个新的提交,并将两个时间戳设置为“现在”。但“现在”是由计算机的时钟决定的。如果您的计算机时钟错误,则时间戳也会错误。
您可以非常轻松地覆盖作者时间戳:许多 Git 命令(包括git commit其本身)都采用一个标志(例如)来设置您想要的任何作者时间戳。覆盖提交者时间戳有点困难,因为没有标志,但实际上并不难,因为读取环境变量。环境变量可以设置为该选项接受的相同类型的字符串值;设置此值会强制提交者时间戳为您喜欢的任何值(即在 Git 可以表示的日期范围内)。--date=dategit commitGIT_COMMITTER_DATE--date
git log按日期排序?有两种方法git log可以进入“想要”一次显示多个提交的情况。一种是当你告诉git log要显示哪些提交时:
git log master develop\nRun Code Online (Sandbox Code Playgroud)\n\n例如,显示分支的提示提交master 和分支的提示提交develop:
I--J <-- develop\n /\n...--F--G--H <-- master\nRun Code Online (Sandbox Code Playgroud)\n\n哪一个应该git log先显示?理想情况下,可能是J,因为J回到I,然后回到H。实际上,git log选择具有更大提交者时间戳的提交,除非您设置各种其他git log选项来覆盖它。在大多数情况下,这就是提交J,一切都运行良好。
当您的提交图中有合并提交时,会发生另一种情况。合并提交只是具有两个或更多父级的任何提交。(“更多”很少见,并不是特别特别,也不是特别有用;考虑两个父母的情况就足够了。)也就是说,假设我们在某个时刻有这个图:
\n\n I--J <-- master\n /\n...--H\n \\\n K--L <-- feature\nRun Code Online (Sandbox Code Playgroud)\n\n虽然事情是这样的,但在这个存储库中,我们git checkout master然后git merge feature. 如果一切顺利,结果是:
I--J\n / \\\n...--H M <-- master\n \\ /\n K--L <-- feature\nRun Code Online (Sandbox Code Playgroud)\n\n这里的提交M是一种合并提交,这意味着它有多个父级。它的两个父级是 commit J\xe2\x80\x94 (在 Git用哈希 ID \xe2\x80\x94master覆盖名称之前)的旧提示和 commit (仍然是 的提示)。我们现在可以安全地删除该名称,因为我们可以通过从 commit 开始找到 commitmasterMLfeature featureLM并向后工作来找到提交。
如果我们删除 name feature,并且可能添加另一个提交master,我们最终会得到:
I--J\n / \\\n...--H M--N <-- master\n \\ /\n K--L\nRun Code Online (Sandbox Code Playgroud)\n\n现在git log将首先向我们展示 commit N。然后它将移动到N\ 的父级M,并向我们展示M。然后它会......好吧,现在怎么办?
这个技巧git log在这里使用\xe2\x80\x94,并且在我们的git log master develop示例中,实际上\xe2\x80\x94git log实际上使用了优先级队列。该优先级队列最初完全是空的。你跑:
git log ...\nRun Code Online (Sandbox Code Playgroud)\n\n并给它一个起点列表。如果您不提供任何信息,git log则选择当前分支的提示提交作为(单个)起点。Git 将分支名称(如果有)转换为它们的哈希 ID,并将所有哈希 ID 填充到此优先级队列中。
现在队列中有一些条目,Git从队列中取出最高优先级的提交。默认情况下,这是具有最高提交者时间戳的那个。但如果队列中只有一项提交,Git 只会获取该一项提交。没有什么可比较的:队列中只有一个提交!请注意,这会将队列中的唯一提交移出队列,因此现在队列为空。
\n\n这就是git log现在将显示的提交。1 Git 从所有对象数据库中获取实际提交并显示它。获取提交还会给出git log父提交的哈希 ID。Git 将它们放入队列中。2 如果只有一个父项,并且队列因拉出其一项而被清空,那么现在队列中又只剩下一项了。
因此,对于从某个分支尖端开始并向后工作而不进行任何合并的简单链,此队列算法仅按照 Git 使用的向后顺序一次显示每个提交。最后一次提交首先出现,第一个提交最后出现。
\n\n但是,当 Git执行像 之类的合并M提交时,Git 将父级都放入队列中。现在队列有两个条目,因此现在其优先级排序生效。同样,默认优先级是较新的提交\xe2\x80\x94那些具有较高提交者时间戳\xe2\x80\x94的提交到队列的前面。Git 将显示稍后(按提交者日期)提交的下一个提交,并将其放在级放入队列中。如果此提交的父提交的优先级高于队列中任何其他提交的优先级,那么接下来会显示这些父提交。
换句话说,实际的 git log循环不仅仅是显示提交、显示父级、显示父级……而是:
git log选项)。git log选项)。队列本身决定提交的显示顺序,但父链接决定提交进入队列的能力。您可以使用命令行命令来启动队列git log。之后,父链接和队列机制接管。
1根据 的选项git log,也许它实际上不会显示提交。不过,让我们把这个问题留给其他问题吧。
2同样,这里可能会很复杂,但我们还是坚持使用简单的模型。请记住,它git log可以执行 Git 所谓的历史简化,它可以省略分支的一些分支,并且不费心显示一些提交。另外,Git 不会在一次中两次显示相同的提交git log,因此,如果这会将已显示的提交放入队列中,那么它现在不会将其放入。
虽然git log默认使用提交者时间戳对提交进行排序,但仅当“要显示”队列中有多个条目时才会发生这种情况。每个提交的父哈希 ID 在提交显示时放入队列中。因此,对于简单的线性链,git log只需按其内部的向后顺序遍历这些链即可。它主要在排序顺序变得重要的合并中(或者,当然,如果您使用多个分支名称,或用于--all查看所有分支)。
日期和时间戳可能是错误的(因为计算机的时钟错误)或被欺骗(出于好或坏的原因故意)。因此,即使没有发生任何恶意行为,您也不能依赖这些。
\n\n选项绘制父/子关系的粗略 ASCII 表示,也强制改变优先--graph级队列中的优先级。在 下,毕竟仅显示父提交git log--topo-order--topo-order显示的子提交都已显示后才会显示。
| 归档时间: |
|
| 查看次数: |
2743 次 |
| 最近记录: |