Rob*_*bin 2 git version-control
我对git在合并后如何存储历史感到有些困惑。
我已成功合并分支A到分支B。现在,当我转到branch中的文件时,B这是合并的一部分,我可以看到该文件在branch上的所有历史记录,A但是看不到branch的任何历史记录B。我该分支文件的历史记录B去了哪里?
合并的方式是通过的,git merge <branch>因此在这种情况下,我处于分支B并已使用git merge A。
例如,在分支A我有如下的提交:
a,aa,aaa分别对应不同的文件。
在分支B,我有如下的提交:
b,bb,bbb分别对应不同的文件。
现在,当我合并分支A到分支B,所有我在分公司看到B git log的a,aa,aaa历史。我看不到我的b历史。
本质上,我希望我的合并是线性的,当我合并A到时,B我希望历史记录具有所有分支B历史记录,并且在历史记录的顶部将是与SVN相似的合并。
我当前的git日志历史非常令人困惑。
历史没有消失,Git只是没有显示它。
在Git中,历史记录就是提交的集合。没有文件历史!
当你执行一个命令git log dir/sub/file.ext,或为此事,git log dir/sub或git log .同时dir/sub,Git会合成一个(临时)文件的历史,从真实的历史,一组提交的提取某些子历史。该合成过程故意删除了一些提交。例如,它将删除所有不影响您所问文件的提交。但是默认情况下,它通过git log称为History Simplification的东西的降幅要大得多。
每个提交都有唯一的哈希ID。例如,您会在git log输出中看到这些。哈希ID实际上只是提交内容的加密校验和。
每个提交都存储(文件的哈希ID)快照(Git将其称为树)。合并提交也是如此:合并提交与其他任何提交一样都具有树。
每个提交还存储您的姓名(作者和提交者),电子邮件地址和时间戳,以便Git可以向您显示这些内容。无论您提供什么,它都会存储一条日志消息,以便Git也可以显示该消息。
Git存储在提交中的最后一件事–实际上,tree紧接在第二件事之后–是父提交的列表,其父列表具有其唯一的哈希ID。
在处理普通的非合并提交时,很容易查看历史记录。我们只是从最新的提交开始,由一些分支名称(如)标识master,然后向后工作。分支名称包含最后一个提交的哈希ID(分支的尖端),我们说分支名称指向该提交:
... <--1234567... <--master
Run Code Online (Sandbox Code Playgroud)
如果commit 1234567是的技巧master,则git log可以向您显示commit 1234567...,并且commit中1234567包含之前 紧接的commit的哈希ID 1234567。
如果我们将真实的哈希ID换成单个字母,为了使事情变得简单,我们将得到如下所示:
A <-B <-C <-D <-E <-F <-G <--master
Run Code Online (Sandbox Code Playgroud)
提交G指向commitF , which points back toE , and so on until we reach the very first commit, commitA . 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 callsA`的根提交:没有父项的提交。
很容易显示线性历史记录,从时间的结尾开始并在开始时结束。Git只是一次选择一个提交并显示它。那是什么:
git log master
Run Code Online (Sandbox Code Playgroud)
这样做:它以标识的一个提交开始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
Run Code Online (Sandbox Code Playgroud)
现在,假设您正在运行git log master(注意:无--patch选项)。Git当然应该显示您M首先提交。但是,Git接下来将显示哪个提交? J或G?如果它显示其中之一,那之后应该显示哪一个?
Git对这个问题有一个一般性的答案:当它向您显示合并提交时,可以将提交的两个父项都添加到“尚未显示的提交”队列中。当它向您显示普通的非合并提交时,它将(单个)父级添加到同一队列中。然后,它可以遍历队列,显示您一次提交一个,将其父母添加到队列中。
如果历史记录是线性的,则队列中一次只有一个提交:删除并显示了一个提交,现在队列中只有一个父提交,您可以看到父提交。
历史记录合并后,队列以一次提交开始,Git将提交从队列中弹出并显示出来,并将两个父级都放入队列中。然后,Git选择两个父母之一,并向您显示G或J,然后将F或I放入队列。队列中仍然有两个提交。Git弹出一个并显示该提交,然后再提交另一个。
最终F,当F已经在队列中时,Git尝试放入队列。Git的避免了添加了两次,所以最终队列深度减小到一个再次提交,在这种情况下的表现F,E,D,等等。(这里的细节有点复杂:该队列特别是一个优先级队列,其优先级由其他git log排序参数确定,因此可以通过不同的方式进行。)
git log --graph如果添加--graph到git log命令中,Git会绘制一些粗略的ASCII码图形,并用线将子提交与父提交连接起来。告诉您正在查看的提交历史毕竟不是线性的,这非常有帮助,即使一次git log只向您显示一次提交(因为必须这样做)。
我在上面提到过,使用-p或时--patch,git log将通过比较父级的快照/树与子级的快照/树来显示提交中的更改。但对于合并提交,有2(甚至更多)家长:有没有办法给你看的比较的父VS孩子,因为有至少2周的父母。
git log默认情况下,什么是完全放弃。它根本没有显示补丁。其他命令执行的操作更为复杂,您也可以说服git log它执行此操作,但仅注意默认值是git log在此处放弃的。
git log文档的可单击链接)运行时git log file.ext,Git会故意跳过diff(通过比较父级和子级获得的diff)不接触的所有非合并提交file.ext。这很自然:如果您有这样的连锁店:
A--B--C--D--E <-- master
Run Code Online (Sandbox Code Playgroud)
并且您file.ext在进行提交A和时进行了更改(或首次创建)E,您只希望看到这两个提交。Git可以通过找出D-vs- 的补丁程序E并查看已file.ext更改的内容(因此应该显示 E),然后转到来完成此操作D。该C航班吗D比较显示没有改变file.ext,所以Git的不会显示D,但它会把C优先级队列中,并继续参观C。该文件也没有任何更改,因此Git最终移至B,没有任何更改,Git移至A。为了进行比较,其中的所有文件A都是新文件,这是任何root提交的规则;所有文件已添加-Git向您显示A 也一样
但是,我们只是看到默认情况下git log不喜欢为合并计算补丁。太难了!因此git log通常不会在此处显示合并。但是,它确实试图简化提交图的任何部分。正如文档所述,默认模式为:
如果最终结果相同,则修剪一些侧枝...
如果提交是合并,并且[文件与]中的一个父级相同,则仅遵循该父级。...否则,请跟随所有父母。
因此,在合并提交就像M在我们的图形,Git会做一个快速检查:是file.ext在同M在G?如果是这样,请添加G到队列中。如果不是,它是在同M在J?如果是这样,请添加J到队列中。否则-即file.ext在不同的M比在两个 G 和 J -add既G和J到队列中。
历史简化还有其他模式,您可以使用各种标志进行选择。这个答案已经太长了,因此我将其留给文档(请参见上面的链接)。
由于Git的历史简化,您不能从显示的内容中得出太多推论。如果您想查看所有内容,请考虑运行。该选项出于目的拆分每个合并(此选项附带),并且强制Git始终跟踪所有父级。git log -- pathgit log --full-history -m -p -- path-mgit diff-p--full-history