git 日志在合并 2 个分支后如何组织我的提交历史记录?

How is git log organizing my commit history after merging 2 branches?

我有两个 git 分支生成这样的日志:

Branch A
commit xyz @ 11:00 am
commit yut @ 11:10 am
commit mot @ 11:30 am

Branch B
commit xyz @ 11::00 am
commit shu @ 11: 20 am
commit yam @ 11: 40 am

Merge Branch B to Branch A

现在查看Branch A的日志,显示如下:

Branch A
commit xyz @ 11:00 am
commit yut @ 11:10 am
commit shu @ 11: 20 am
commit mot @ 11:30 am
commit yam @ 11: 40 am
commit merge Branch B 11:50 am

但是当我检查 shu 的提交对象时,它的父哈希是 xyz 而不是 yut(尽管 yut 在日志中位于它前面)。

git 日志功能是否只是读取两个分支的提交历史(通过合并提交),然后按 'time' 提交的顺序显示它们?

世界各地每台计算机的本地时间差异不会打乱这个顺序吗?

如果你想查看哪个提交是哪个提交的父提交,如果你添加 --graph 选项,事情应该会更清楚:

git log --graph

关于提交的显示顺序,请参阅git help log中的Commit ordering部分:

默认顺序是:

--date-order

Show no parents before all of its children are shown, but otherwise show commits in the commit timestamp order.

除了,值得考虑git log 如何显示提交。这很重要,因为 git log 实际上 确实 实现了其中的许多选项。

请记住,每个提交本身都使用唯一的哈希 ID 进行编号,并且每个提交都存储其 [=237= 的数字(哈希 ID) ](前任)提交或提交。大多数提交都有一个 parent,这会产生一个漂亮、简单的线性(但 backwards-looking)提交链:

... <-F <-G <-H

其中 H 是链中的 last 提交。此处的大写 H 代表提交的实际哈希 ID(无论是什么)。 commit Hcontents 分为两部分:

  • 提交的主要数据包含构成该提交的所有文件的快照。
  • 该提交的 元数据 包含提交作者的姓名和电子邮件地址等信息。在此元数据中,提交 H 存储早期提交 G.
  • 的实际哈希 ID

那么,我们说提交 H 指向 之前的提交 G。 Git 使用哈希 ID 来查找实际提交,在其所有 Git objects 存储库中的大数据库中,因此只要我们给出 Git 哈希 ID H,可以用它来读出内容,包括hash ID G。然后它有一个 G 的哈希 ID,它可以用来读取 G 的内容,其中包括哈希 ID F,它可以用来读取 F的内容等等。

Git获取H的hash ID的地方是分支名:

... <-F <-G <-H   <--master

例如。我们说分支名称 指向 链中的最后一次提交。

合并提交的特别之处在于它有两个parent而不是单个parent。也就是说,合并提交指向(向后,一如既往)到两个提交而不是一个:

...--I--J   <-- branch1
         \
          M   <-- merged
         /
...--K--L   <-- branch2

在这里,我们创建了一个分支名称merged,指向提交M。从 M,我们可以倒退到 either J or L,当我们工作一次提交时一次(Git 倾向于这样做)。

但是,如果像 git log 所希望的那样,我们想要查看 both 提交 J L?如果我们被迫一次处理一个提交——就像我们经常做的那样——我们将如何管理它?

Git 使用的方法是将提交哈希 ID 放入 queue, or more specifically, a ]priority queue](https://en.wikipedia.org/wiki/Priority_queue)。也就是说,如果我们在分支 merged 上并且我们 运行 git log、Git:

  1. 使用名称merged查找M的提交哈希ID,并将M放入队列。队列现在只有一个条目。

  2. 从队列中取出第一个提交(使队列为空)并检查它。这个提交是一个合并提交,所以git log不会打印太多,至少在默认情况下是这样——也就是说,如果你用git log -p请求补丁你没有得到一个——但是它确实两个parents放入了队列。

    由于队列有一个顺序 属性——优先级较高的提交在优先级较低的提交之前使用——优先级决定接下来显示 JL 中的哪一个。

  3. 从队列中取出第一个提交(在队列中留下一个提交)并检查它。这个提交,无论是 J 还是 L,都是一个 普通提交 和一个 parent,所以 Git 打印它并且如果您要求一个补丁,则包括一个补丁。1 然后 Git 将此提交的 parent,即 parent 放入队列中(这样队列中又有两个条目)。

  4. 重复上面的“从队列中获取第一个提交并使用它”直到队列最终为空——这可能需要 long 时间!——或者你告诉 Git 退出。

您看到提交的顺序因此取决于您设置的优先级。

不过,除了各种排序选项之外,还有一个非常重要的选项 --first-parent,它会影响 Git 处理合并提交的方式。特别是,我们在上面的步骤 2 中看到 Git 将合并提交 Mboth parent 放入队列。如果我们使用 --first-parent,Git 不会 将两个 parent 都放入队列中。相反,Git 仅将 M 第一个 parent 放入队列。

合并的第一个parent因此非常重要。但是哪个 parent 是第一个呢?答案取决于 如何 进行合并的人进行合并。

当你 运行:

git checkout somebranch
git merge otherbranch

和Git实际上像M一样进行合并提交,新合并提交的第一个 parent是您检查的提交在 git checkout 步骤中输出:最后一个 c在进行合并之前提交 somebranchsecond parent——git log --first-parentignore 的那个——是另一个分支的最后一次提交(当时你运行 git merge,即)。所以如果你有:

...--G--H--I   <-- somebranch (HEAD)

 ...--J--K--L   <-- otherbranch

——这里的(HEAD)表示你做了一个git checkout somebranch并得到了提交I——和运行git merge otherbranch,新的合并提交将有提交 I 作为其 第一个 parent,并 L 作为其第二个:

...--G--H--I--M   <-- somebranch (HEAD)
             /
 ...--J--K--L   <-- otherbranch

请注意,M 现在是 最后一个 提交 somebranch,提交 J-K-L 现在是 both分支。因此,git log 命令将显示提交 J-K-L ,除非您使用 --first-parent,特别是 排除 这些提交因为他们来自支行。

请注意,使用 git pull 可能会导致某些人称之为 ,其中 git log --first-parent 向您显示您 不希望 的提交看,而不是你做的那些。那是因为git merge你的(本地)支线当成主线,而不是把其他Git的支线当主线,你的工作当成feature-branch。另一方面,这可以是您 喜欢的顺序。作为控制者(进行合并的人),由您决定是否有偏好,如果有,偏好是什么。