合并后Git的分支历史

时间:2018-02-15 18:35:30

标签: git version-control

我对git在合并后如何存储历史感到困惑。

我已成功将分支A合并到分支B。现在,当我转到文件时,在分支B中,这是合并的一部分,我看到分支A的该文件的所有历史记录,但我没有看到分支{{的历史记录1}}。我的分支B的文件的历史记录在哪里?

我合并的方式是B,所以在这种情况下,我在分支git merge <branch>并使用了B

例如,在分支git merge A中,我有以下提交: 对应不同文件的Aaaa

在分支aaa中,我进行了以下提交: 对应不同文件的Bbbb

现在,当我将分支bbb合并到分支A时,我在分支B中看到的只有B git logaaa历史记录。我没有看到aaa历史记录。

本质上,我希望我的合并是线性的,当我将b合并到A时,我希望历史记录具有所有分支B历史记录并且在历史记录之上刚刚发生的合并就像SVN一样。

我目前的git日志历史非常令人困惑。

2 个答案:

答案 0 :(得分:2)

它应该仍然存在。尝试使用git log --graph查看。

答案 1 :(得分:2)

TL; DR

历史记录 ,Git只是

在Git中,历史记录提交的集合。没有档案历史记录!

当您在git log dir/sub/file.ext中运行git log dir/subgit log .dir/sub之类的命令时,Git将合成 a (临时)文件历史记录,通过从真实历史中提取一些子历史 - 提交集。这个合成过程故意丢弃一些提交。例如,它会删除所有提交,这些提交不会影响您询问的任何文件。但默认情况下,通过git log调用历史简化的内容,它会比这更多。

较长的

每次提交都有唯一的哈希ID。例如,您可以在git log输出中看到这些内容。哈希ID实际上只是提交内容的加密校验和。

每个提交存储(哈希ID)文件的快照-Git称之为。合并提交也是如此:合并提交与任何其他提交一样,都有一个树。

每次提交还会存储您的姓名(作者和提交者)以及电子邮件地址和时间戳,以便Git可以向您显示这些内容。它存储了一条日志消息 - 无论你给它什么 - 所以Git也可以显示它。

Git存储在提交中的最后一件事 - 第二件事,就在tree之后 - 就是提交的列表,通过它们唯一的哈希ID。

线性历史很容易

在处理普通的非合并提交时,查看历史非常简单。我们只是从最新的提交开始,由某些分支名称master标识,并向后工作。分支名称包含最后一次提交的哈希ID - 分支的提示 - 我们说分支名称​​指向提交的

... <--1234567...   <--master

如果提交1234567master的提示,则git log可以显示提交1234567 ...并且提交1234567内部包含哈希ID 1234567之前的提交。

如果我们换掉单个字母的真实哈希ID,为了方便起见,我们会得到这样的结果:

A <-B <-C <-D <-E <-F <-G   <--master

G点提交回commit F , which points back to E , and so on until we reach the very first commit, commit A . This commit does not point anywhere—it *can't*, it was the first commit; it cannot have a parent—so this is where the history ends (starts?), at the beginning of time. Git calls A` a root 提交:提交没有父母。

从时间结束开始到结束时,很容易显示线性历史记录。 Git只是一次挑出一个提交并显示它。那是什么:

git log master

确实:它从master标识的一个提交开始,并显示它,然后显示一个提交的一个父级,然后显示之前的那个,依此类推。

当你让Git向你展示提交时,你可以 - 事实上,你几乎总是让Git将它显示为补丁,而不是快照。例如,git log --patch就是这样做的。为了将提交显示为补丁,Git首先查看提交的树,然后在提交树中查看,并比较两者。由于两者都是快照,因此从父母的快照到孩子的任何更改都必须是使孩子提交的人实际做的任何事情。

非线性历史更难

现在我们知道Git倒退了,让我们来看看更复杂的历史,包括包含实际合并提交的历史记录。 (让git merge并不总是合并的事实让我们不要偏离!)

合并提交只是一个至少有两个父级的提交。在大多数情况下,你不会看到有三个或更多父母的提交--Git称这些章鱼合并,他们不会做任何你不能做普通合并的事情,所以章鱼合并是主要是为了炫耀你的Git-fu。 : - )

我们通常通过git checkout somebranch; git merge otherbranch进行合并,我们可以像这样绘制生成的提交链:

...--E--F--G------M   <-- master
         \       /
          H--I--J   <-- feature

现在,假设您运行git log master(注意:没有--patch选项)。 Git当然应该首先显示提交M。但接下来Git会展示哪些提交? JG?如果它显示其中一个,那之后应该显示哪一个?

Git对此问题有一个通用的答案:当它向您显示合并提交时,它可以将提交的两个父项添加到&#34;提交尚未显示的队列&#34 ;。当它向您显示普通的非合并提交时,它会将(单个)父级添加到同一队列中。然后它可以循环遍历队列,显示您一次提交一个,将其父项添加到队列中。

当历史记录是线性的时,队列中一次只有一个提交:一个提交被删除并显示,队列现在有一个父提交,你看到父提交。

当历史记录合并时,队列以一次提交开始,Git从队列中弹出提交并显示它,并将两个父项放入队列中。然后Git选择其中一个父母并向您显示GJ,并将FI放入队列。队列中仍然有两个提交。 Git弹出一个并显示提交并放入另一个。

最终,当F已经在队列中时,Git会尝试将F放在队列中。 Git避免添加两次,因此最终队列深度再次减少到一次提交,在这种情况下显示FED等等。 (这里的细节有点复杂:队列特别是priority queue,优先级由额外的git log排序参数决定,所以有不同的方法可以实现。)

您可以查看git log --graph

的连接

如果您在--graph命令中添加git log,Git会绘制一个粗略的ASCII艺术图表,其中的行将子提交连接回父母。这非常有助于告诉您,您正在查看的提交历史记录毕竟不是线性的,即使git log一次向您显示一个提交(因为它必须)。

显示合并提交

我上面提到过-p--patchgit log会通过比较父母的快照/树来显示提交中已更改的内容针对孩子的快照/树。但是对于合并提交,有两个(或甚至更多)父母:没有办法向您显示 父母与孩子的比较,因为至少有两个父母。

默认情况下,git log的作用是完全放弃。它根本没有显示补丁。其他命令做了一些更复杂的事情,你可以说服git log也这样做,但是请注意默认是git log放弃这里。

History Simplification(这是git log文档的可点击链接)

当你运行git log file.ext时,Git会故意跳过任何非合并提交,其中diff(通过比较父对子获得)不会触及file.ext。这很自然:如果你有一个像:

这样的链条
A--B--C--D--E   <-- master

当您提交file.extA时,您已更改(或首次创建)E,您只想看到这两个提交。 Git可以通过找出D - vs - E的补丁并看到file.ext已更改(因此它应显示 E)来实现此目的,然后转到DC - vs - D比较显示file.ext没有变化,因此Git 赢了显示D,但它会放C位于优先级队列中,然后继续访问C。这也没有改变文件,所以Git最终转移到B,没有任何变化,Git移动到A。为了进行比较,A中的所有文件始终是新的 - 这是任何根提交的规则;所有文件都已添加 - 所以Git也会显示A

我们只是看到,默认情况下git log并不想为合并计算补丁。太难了!所以git log通常不会在这里向您展示合并。但是,它会尝试简化提交图的任何部分。正如文档所述,默认模式:

  如果最终结果相同,

修剪一些侧枝...

     

如果提交是合并,并且[该文件与一个父项中的文件相同],请仅关注该父项。 ...否则,跟随所有父母。

因此,在我们图表中的M合并提交时,Git会快速检查:file.extM中的G相同吗?如果是,请将G添加到队列中。如果不是,MJ中的相同吗?如果是,请将J添加到队列中。否则 - file.ext中的M中的G不同 J G - 同时添加J 1}}和git log -- path到队列。

历史简化还有其他模式,您可以使用各种标志进行选择。这个答案已经太长了,所以我将把它们留给文档(见上面的链接)。

结论

由于Git执行的历史简化,您无法从git log --full-history -m -p -- path向您展示的内容中得出太多推论。如果您想查看所有内容,请考虑改为运行-mgit diff选项将每个合并拆分为-p目的(这与--full-history选项一致),{{1}}强制Git始终跟随所有父母。