考虑这个测试脚本。
#!/bin/sh -x
#initialize repository
rm -rf missing-merge-log
mkdir missing-merge-log
cd missing-merge-log
git init
# create files, x, y, and z
echo x > x
echo y > y
echo z > z
git add -A .
git commit -m "initial commit"
# create a branch
git branch branch
# change x and z on master
echo x2 > x
echo z2 > z
git commit -am "changed x to x2, z to z2"
git log master -- x
# change y and z on the branch
git checkout branch
echo y2 > y
echo z3 > z
git commit -am "changed y to y2, z to z3"
# merge master into branch
git merge master
# resolve z conflict
echo z23 > z
git add z
# undo changes to x during merge conflict resolution
# (imagine this was developer error)
git checkout branch -- x
git commit --no-edit
# merge branch into master
git checkout master
git merge branch
# now the x2 commit is entirely missing from the log
git log master -- x
Run Code Online (Sandbox Code Playgroud)
我们首先创建三个文件,x,y,和z,并创建一个名为分支branch。在 中master,我们提交了对xand的更改z,在分支中,我们提交了对yand的更改z。
然后,在分支中,我们从 合并master,但在合并冲突解决期间,我们将更改恢复到x。(就本示例而言,假设这是一个开发人员错误;开发人员不打算拒绝对 的更改x。)
最后,回到master,我们合并来自分支的更改。
在这一点上,我希望 forgit log x显示三个更改:初始提交、x对 master的更改以及将更改恢复为x.
但是相反,在脚本的末尾,git log只显示对 x 的初始提交,没有给出任何x修改过的迹象!这使用 git 版本 2.22.0。
为什么git log要这样做?是否有参数git log -- x可以显示这里发生了什么?git log --all -- x没有帮助。
(git log --all确实显示了所有内容,但在现实生活中会显示所有文件的所有更改,包括与y和无关的更改z,这太难了。)
使用--full-history- 但您可能还需要更多选项,所以请继续阅读。
首先,非常感谢复制脚本!这在这里非常有用。
下一个:
(
git log --all确实显示了所有内容,但在现实生活中会显示所有文件的所有更改,包括与y和无关的更改z,这太难了。)
是的。但它表明任何提交都没有问题;问题完全出git log在这里。它与可怕的历史简化模式有关,它:
git log master -- x
调用。
git log 没有历史简化让我添加以下输出:
git log --all --decorate --oneline --graph
Run Code Online (Sandbox Code Playgroud)
("git log with help from A DOG"),因为我使用脚本进行复制将具有与您(或其他任何人进行另一个复制)不同的哈希 ID,但具有相同的结构,因此让我们谈谈关于提交:
* cc7285d (HEAD -> master, branch) Merge branch 'master' into branch
|\
| * ad686b0 changed x to x2, z to z2
* | dcaa916 changed y to y2, z to z3
|/
* a222cef initial commit
Run Code Online (Sandbox Code Playgroud)
现在,一个正常的git log,无需-- x检查文件x,不会打开历史简化。Git 从你指定的提交开始——例如:
git log dcaa916
Run Code Online (Sandbox Code Playgroud)
开始于dcaa916- 或者HEAD如果您没有指定任何内容。
在这种情况下,git log从 commit 开始cc7285d。Git 显示该提交,然后移至该提交的父级。这里有两个父级——dcaa916而且——所以ad686b0Git 将两个提交都放入一个优先级队列。然后它从队列的头部拉出一个提交。当我尝试这个时,它拉出的是dcaa916. (在更现实的图表中,默认情况下它将使用具有较晚提交者时间戳的那个,但是使用脚本构建此存储库后,两个提交具有相同的时间戳。)Git 显示该提交并将dcaa916的父项a222cef放入队列中。对于拓扑完整性,给定这个特殊的图,队列前面的提交现在总是ad686b0,所以 Git 显示提交,然后......
那么,现在,母公司ad686b0是a222cef,但a222cef已经是在队列!这就是“为了拓扑完整性”的用武之地。通过不要a222cef过早显示,我们可以确保我们不会意外地显示a222cef两次(以及其他问题)。队列现在已经a222cef在里面了,没有别的了,所以git log取出a222cef队列,显示a222cef,并将a222cef的父母放入队列中。在这个复制器示例中,没有父级,因此队列保持为空,并且git log可以完成,而这正是我们在常规git log. 在 A DOG 的帮助下,我们也得到了图形和单行输出变量。
git log 历史简化Git 没有文件历史记录。存储库中的历史记录由commits组成。但git log会尽力显示文件历史记录。要做到这一点,它必须综合一个,为此,Git 的作者选择简单地省略一些提交的子集。该文档试图用一句话来解释这一点:
有时您只对部分历史感兴趣,例如修改特定 <path> 的提交。但是History Simplification有两部分,一部分是选择提交,另一部分是如何做,因为有各种策略可以简化历史。
我认为这种单段解释是行不通的,但我还没有想出我认为正确的解释。:-) 他们在这里试图表达的是:
Git 不会向您展示所有提交。它将显示一些选定的提交子集。
这部分非常有意义。我们已经看到,即使没有历史简化:Git 从最后一次提交开始,我们用分支名称HEAD或其他方式指定的那个提交,然后向后工作,一次一个提交,一次将多个提交放入其必要时优先队列。
使用历史简化,我们仍然使用优先级队列遍历提交图,但对于许多提交,我们只是不会显示提交。到目前为止还好——但现在 Git 出现了转折,导致他们写了那个奇怪的段落。
如果 Git 不打算向您展示所有提交,那么它可能会作弊,甚至懒得跟踪某些分支。
这是很难表达的部分。当我们从分支尖端向提交图根向后工作时,每个合并提交,其中两个提交流连接,变成一个分支,其中两个提交流发散。特别是,commitcc7285d是一个合并,当我们没有进行历史简化时,Git 总是将父母双方都放入队列中。但是当我们确实进行了历史简化时,Git 有时不会将这些提交放入队列中。
这里真正棘手的部分是决定哪些提交进入队列,这就是文档的“更详细的解释”和TREESAME概念的用武之地。我鼓励人们通读它,因为它有很多很好的信息,但它非常密密麻麻,一开始就不太擅长定义TREESAME。文档是这样写的:
假设您指定
foo为 <paths>。我们将调用修改foo!TREESAME 的提交,其余的 TREESAME。(在过滤为 的差异中foo,它们分别看起来不同和相等。)
这个定义取决于提交是非合并提交!
所有提交都是快照(或更准确地说,包含快照)。因此,任何提交都不会自行修改任何文件。它只是有文件,或者没有文件。如果有文件,它有一些特定内容的文件。要将提交视为更改(作为一组修改),我们需要选择其他一些提交,提取两个提交,然后比较两者。对于非合并提交,有一个明显的提交可以使用:父提交。鉴于一些提交链:
...--F--G--H--...
Run Code Online (Sandbox Code Playgroud)
我们将通过提取和并比较它们来查看提交中发生了什么变化。我们会看到什么改变在通过提取和,并比较他们。这就是此处 TREESAME 段落的内容:我们采用and ,比如说,并删除除您询问的文件之外的所有文件。然后我们比较剩余的文件。它们在精简版和? 中是否相同?如果是这样,并且是 TREESAME。如果不是,他们不是。HGHGFGFGFGFG
但是根据定义,合并提交至少有两个父项:
...--K
\
M
/
...--L
Run Code Online (Sandbox Code Playgroud)
如果我们在合并提交M,我们选择哪个父节点来确定什么是 TREESAME,什么不是?
Git 的答案是将提交与所有父项进行比较,一次一个。有些比较可能会导致“是 TREESAME”,而另一些比较可能会导致“不是 TREESAME”。例如, file fooinM可能匹配 file fooinK和/或 file fooin L。
Git 使用的提交取决于您提供给的附加选项git log:
默认模式
如果提交对任何父级都不是 TREESAME,则包括在内(尽管这可以更改,请参见
--sparse下文)。如果提交是合并,并且对一个父级是 TREESAME,则仅关注该父级。(即使有多个 TREESAME 父母,也只关注其中一个。)否则,关注所有父母。
因此,让我们考虑 merge cc7285d,并将其与其(两个)父项中的每一个进行比较:
$ git diff --name-status cc7285d^1 cc7285d
M z
$ git diff --name-status cc7285d^2 cc7285d
M x
M y
M z
Run Code Online (Sandbox Code Playgroud)
这意味着git log将只走第一个父级,提交cc7285d^1(即dcaa916)——这是一个不会改变的x:
...如果提交是合并,并且对一个父级是 TREESAME,则仅关注该父级。...
所以这 git log走 commit cc7285d,然后 commit dcaa916,然后 commit a222cef,然后停止。它从不查看提交cc7285d^2(即ad686b0)。
git log文档的这一部分的其余部分描述了选项--full-history、--dense、--sparse和--simplify-merges(甚至我也不明白最后一个选项的真正目的:-))。在所有这些中,--full-history是最明显的并且会做你想做的。(--ancestry-path和--simplify-by-decoration在本节还有,但不影响合并时路径。)
虽然--full-history将确保 Git 遍历每个合并的所有“分支”,但git log -p默认情况下它本身不显示合并提交的差异。您必须添加三个选项-之一-c,--cc或-m以诚化妆git log -p显示在所有任何差异的任何合并。
如果您的目标是专门找到一个糟糕的双父合并,即删除一些本应保留的特定更改的合并,您可能希望显示该合并与其两个父合并中的至少一个或两者的差异。该git show命令将执行此操作,但其默认值是--cc样式。该git log命令根本不会这样做。如果您添加--cc到您的git log,您将获得与git show默认情况下显示的相同的差异 - 这也不会起作用。
该--cc或-c选项告诉Git的是,在合并看时提交,Git的差异比较应提交对所有的父母,然后产生一个总结差异,而不是一个详细的一个。摘要的内容不包括与一个或所有父项匹配的部分。您正在寻找意外删除重要更改的合并 -至少与其父项之一相同的合并,但它应该与该父项不同。这个组合的差异将隐藏变化不是但应该发生的地方。所以你不想要-cor --cc。
这就留下了-m选择。当git showorgit log将显示差异,并且提交是合并提交时,Git 将显示每个 parent 一个差异。也就是说,对于像这样的合并提交M,git show -m将首先比较KvsM并显示差异。然后它将比较LvsM并显示另一个差异。 对于这种特殊情况,这就是您在这里想要的选项。
请注意,-m与 很好地结合--first-parent以仅显示与每个合并的第一个父级的完整差异。通常,这正是您想要的。
| 归档时间: |
|
| 查看次数: |
558 次 |
| 最近记录: |