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

nod*_*bqs 2 git merge logging commit

我有两个生成日志的 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
Run Code Online (Sandbox Code Playgroud)

现在,当我检查分支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
Run Code Online (Sandbox Code Playgroud)

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

git log 功能是否只是读取两个分支的提交历史记录(通过合并提交),然后按提交的“时间”顺序显示它们?

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

tor*_*rek 7

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

\n

请记住,每个提交本身都使用唯一的哈希 ID 进行编号,并且每个提交都存储其父(前驱)提交的哈希 ID\xe2\x80\x94 的数字\xe2\x80\x94 。大多数提交都有一个父提交,它会产生一个漂亮、简单的线性(但向后看)提交链:

\n
... <-F <-G <-H\n
Run Code Online (Sandbox Code Playgroud)\n

链中的最后一次H提交在哪里。这里的大写字母代表提交的实际哈希 ID(无论是什么)。commit 的内容分为两部分:HH

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

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

\n

HGit 获取hash ID 的地方是从分支名称:

\n
... <-F <-G <-H   <--master\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

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

\n
...--I--J   <-- branch1\n         \\\n          M   <-- merged\n         /\n...--K--L   <-- branch2\n
Run Code Online (Sandbox Code Playgroud)\n

在这里,我们创建了一个merged指向 commit 的分支名称M。当我们一次处理一个提交时,我们可以从M向后退到 J 之一(正如 Git 倾向于做的那样)。 L

\n

但是,如果我们想同时git log查看提交呢?如果我们被迫一次处理一个提交\xe2\x80\x94,就像我们经常做的那样\xe2\x80\x94,我们将如何管理呢?J L

\n

Git 使用的方法是将提交哈希 ID 放入队列或者更具体地说,优先队列]( https://en.wikipedia.org/wiki/Priority_queue )。也就是说,如果我们在分支上merged运行git logGit:

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

    \n
  2. \n
  3. 从队列中取出第一个提交(使队列为空)并检查它。这个提交是一个合并提交,所以git log不会打印太多关于它的信息,至少默认情况下是这样的,也就是说,如果你要求补丁,git log -p你不会得到一个\xe2\x80\x94,但它确实如此父母双方放入队列中。

    \n

    由于队列具有排序属性\xe2\x80\x94,因此优先级较高的提交会先于较低优先级的提交\xe2\x80\x94,因此优先级决定接下来将显示哪个J或。L

    \n
  4. \n
  5. 从队列中取出第一个提交(在队列中留下一个提交)并检查它。此提交,无论是JL,都是具有单父项的普通提交,因此 Git 会打印它并包含一个补丁(如果您需要的话)。1 然后 Git 将此提交的父级(无论是哪个父级)放入队列中(以便队列中再次有两个条目)。

    \n
  6. \n
  7. 重复上面的“从队列中获取第一个提交并使用它”直到队列最终为空\xe2\x80\x94这可能需要很长时间 \xe2\x80\x94或者你告诉Git退出。

    \n
  8. \n
\n

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

\n

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

\n

因此,合并的第一个父级非常重要但哪位父母是第一位呢?答案取决于谁进行合并,如何进行合并。

\n

当你跑步时:

\n
git checkout somebranch\ngit merge otherbranch\n
Run Code Online (Sandbox Code Playgroud)\n

Git 实际上会进行合并提交,例如M,新合并提交的第一个父级是您在步骤中签出的提交:进行合并之前git checkout的最后一次提交。somebranch第二父分支\xe2\x80\x94git log --first-parent忽略\xe2\x80\x94,这是另一个分支的最后一次提交(即在您运行时git merge)。所以如果你有:

\n
...--G--H--I   <-- somebranch (HEAD)\n\n ...--J--K--L   <-- otherbranch\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\x94(HEAD)这里表示您执行了git checkout somebranch并获得了提交I\xe2\x80\x94 并运行git merge otherbranch,新的合并提交将把提交I作为其第一个父级,并L作为其第二个父级:

\n
...--G--H--I--M   <-- somebranch (HEAD)\n             /\n ...--J--K--L   <-- otherbranch\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,M现在是最后一次提交somebranch,并且提交J-K-L现在在两个分支上。因此,除非您使用,否则该git log命令将显示提交,这会明确排除这些提交,因为它们来自侧分支。J-K-L --first-parent

\n

请注意,使用git pull可能会导致某些人所说的“狐步合并”,其中显示您不想git log --first-parent看到的提交,而不是您所做的提交。这是因为将您的(本地)分支视为主线,而不是将其他 Git 分支视为主线,并将您的工作视为功能分支。另一方面,这可能是您喜欢的顺序。作为控制者\xe2\x80\x94,即进行合并的人\xe2\x80\x94,您可以决定是否有偏好,如果有,该偏好是什么。git merge

\n


LeG*_*GEC 5

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

git log --graph
Run Code Online (Sandbox Code Playgroud)

关于提交的显示顺序,请参阅以下内容中的提交顺序部分git help log

默认顺序是:

--日期顺序

在显示所有子项之前不显示父项,但否则按提交时间戳顺序显示提交。