行删除已经完成,但没有责任归咎于承诺

Lines deletions has been done but there is no commit to blame

某些行已从文件中删除,我想知道它是何时以及如何发生的。

但是:没有发现错误的提交。真奇怪。

这里是 git log,它显示(作为最后更改)添加行时的提交:

$ git log --follow -p -- src/QsGeneralBundle/Repository/EmployeeRepository.php 
commit 80dc439aba2bf38ed7e95888dd21c1ba0f8960ce
Author: Benoit <benoit.guchet@gmail.Com>
Date:   Tue Jul 16 18:31:38 2019 +0200

    Opti Doctrine #814

diff --git a/src/QsGeneralBundle/Repository/EmployeeRepository.php b/src/QsGeneralBundle/Repository/EmployeeRepository.php
index 7dfc2f963..a3e916690 100644
--- a/src/QsGeneralBundle/Repository/EmployeeRepository.php
+++ b/src/QsGeneralBundle/Repository/EmployeeRepository.php
@@ -10,12 +10,15 @@ namespace QsGeneralBundle\Repository;
  */
 class EmployeeRepository extends BaseRepository
 {
-       public function findAllQb() {
-               $qb = $this->createQueryBuilder('e')
+    public function findAllQb() {
+        $qb = $this->createQueryBuilder('e')
             ->innerJoin('e.Account', 'a')
-                       ->orderBy('a.firstname', 'ASC')
-                       ->addOrderBy('a.lastname', 'ASC')
-            ->andWhere('a.isEnabled = TRUE');
+            ->leftJoin('a.SpecificRole', 'r')
+            ->leftJoin('a.Contractor', 'c')
+            ->orderBy('a.firstname', 'ASC')
+            ->addOrderBy('a.lastname', 'ASC')
+            ->andWhere('a.isEnabled = TRUE')
+            ->select('e, a, r, c');

                return $qb;
        }

现在这是文件的当前内容:

<?php

namespace QsGeneralBundle\Repository;

/**
 * Created by IntelliJ IDEA.
 * User: Benoît Guchet
 * Date: 16/11/2016
 * Time: 13:26
 */
class EmployeeRepository extends BaseRepository
{
    public function findAllQb() {
        $qb = $this->createQueryBuilder('e')
            ->innerJoin('e.Account', 'a')
            ->orderBy('a.firstname', 'ASC')
            ->addOrderBy('a.lastname', 'ASC')
            ->andWhere('a.isEnabled = TRUE');

        return $qb;
    }
}

没有本地变化,如 git status:

$ git status src/QsGeneralBundle/Repository/EmployeeRepository.php 
On branch master
Your branch is ahead of 'origin/master' by 11 commits.
  (use "git push" to publish your local commits)
nothing to commit, working tree clean

对这个谜团有什么想法吗?

假设 src/QsGeneralBundle/Repository/EmployeeRepository.php 从未被重命名(或不在将显示的提交范围内),您可以省略 --follow 并添加 --full-history-m.

这里有两个问题:

  • 首先,您要查找的更改可能发生在合并期间。如果没有 -m(或其他一些也不是默认参数),git log 将忽略合并期间提交的 所有 更改。

  • 其次,当你指定一个文件名如src/QsGeneralBundle/Repository/EmployeeRepository.php(有或没有--follow)时,Git开启History Simplification 。当历史简化开启时,Git 只是 懒得去查看 某些提交。特别是,它不会遵循合并的分支(incorectly-但请参阅脚注)不会 采取 您认为合并 应该 [=174] 的更改=]取.

因此,通过强制历史简化保留所有提交 (--full-history) 并强制 git log 将合并提交显示为 two git diff s,你应该能够找到你想要的提交被(错误地)丢弃的地方。


脚注:Git 假设每次合并的结果都是正确的 结果。也就是说,假设合并提交在其 right-side incoming-branch 更改时有错误修复,并且在其 left-side 更改时根本没有更改。进一步假设进行合并的人放弃了错误修复:

o another commit (has bug)
|
* merge commit (has bug, person who merged dropped the fix)
|\
o | main line work (has bug)
| |
| o fix for bug
|/
o main line work (has bug)

Git 假设 right-side 更改(错误修复)是 介绍 错误,进行合并的人很聪明并省略了,保留了 left-side 版本(根据进行合并的人,因此现在根据 Git也是)是 "correct"。事实上,合并提交是 错误的 ... 但是当 Git 进行历史简化时,Git 认为它是正确的。因此 Git 此时仅跟随提交图的左侧。你永远看不到错误修复!

查看修复提交的唯一方法是避免或禁用历史简化(省略文件名,或添加 --full-history)。但是由于默认情况下,git log -p 不会将合并提交与 bug-fix 提交进行区分,即使这样也是不够的。


附加脚注:请注意 Git 中的文件名是 完整路径 。如果一个文件被命名为 a/b/c.ext,现在被命名为 a/d/c.ext,这个文件有两个不同的名字。 Git不存储目录/文件夹,即why you cannot store an empty directory。 (脚注的旁注:每个存储库中的一个空树对象,但使用它会导致稍后出现问题,因为Git一直试图将其转换为子模块——参见有关详细信息的链接问题。)

Git 处理您的计算机坚持使用目录/文件夹的方式——这就是允许您的 OS 提供 的方式——是宠爱你的 OS。 Git 只是提取 文件 进行提交,如果您的 OS 要求创建 Git-name 为 a/b/c.ext 的文件作为文件夹 b 中的文件 c.ext 在文件夹 a 中,然后,Git 创建文件夹 a,然后创建文件夹 a/b,以便创建一个名称为 a/b/c.ext 的文件。如果有没有个名称以a/b开头的文件,Git就不会创建 a/b首先。

无论如何,--follow 的根本问题是它的实现是一个俗气的 hack。假设历史——即存储库中的 backwards-looking 提交链——看起来像这样,后面的提交向右:

...--I--J
         \
          M--N   <-- branch
         /
...--K--L

其中每个字母代表一个实际的提交散列。在提交 N 中,您有一个名为 a/d/c.ext 的文件;在提交 I 中,同一个文件的名称为 a/b/c.extgit log --follow a/d/c.ext处理这个的方式就是看每一个commit-pair,一步一个脚印:

  • 首先,Git查看M-N对。

    M中的文件名为a/b/c.extN中的文件名为a/d/c.ext吗?如果是这样,那么一旦 Git 在 J-ML-M 对上工作,Git 将 停止 寻找 a/d/c.ext 并开始寻找 a/b/c.ext(旧名称,而不是当前名称)。

    如果不是——如果在M中仍然是a/d/c.ext——那么当Git比较JM,或者比较LM,它将查找 a/d/c.ext(当前名称,而不是旧名称)。

  • 接下来,Git 查看 J-ML-M 对——或者,如果进行历史简化而不是强制 not to, 挑一对看,并没有真正看另一对,也没有跟随分支的那条腿。

    如果文件在其中一对中重命名,而不是另一对,Git 真的无法处理。没有 --full-history Git 将选择一条腿跟随,并且在某种程度上假装没有合并并且重命名检测可以 wrk(虽然我不确定它确实如此,但考虑到 git log 在合并周围显示的其他奇怪之处)。如果文件没有在 对中重命名,但是,我们仍然很好:文件仍然有它的新名称,所以如果我们强制 --full-history,那么 Git 现在必须遍历合并的两个分支,Git 会这样做。假设它现在执行 I-J 部分:

  • 现在 Git 比较提交 IJ。文件在这里重命名了吗? 假设它在这里被重命名了。实际上,Git 对自己说:啊哈,让我们停止寻找 a/d/c.ext;从现在开始,让我们寻找 a/b/c.ext. 当 Git 将提交 I 与提交 I 之前的任何内容进行比较时,就可以了。

  • 但是,在跟踪合并的这段时间后,Git 现在——无论如何在 --full-history 下——返回并比较提交 KL,来自合并的 other 分支。 Git 去检查文件 a/b/c.ext,因为这是它现在认为文件具有的路径。 但是文件在合并的这个分支中仍然有a/d/c.ext名称。结果是git log --follow正在寻找错误的文件!

要使这一切正常进行,git log --follow 需要比实际情况更聪明--follow 所做的只是更改 它正在寻找的一个文件名 ,并且它不记得它在某些特定的 部分 中这样做了] 的提交图。 Git 需要以某种方式将名称更改与图形遍历联系起来,并且 "undo" 每当它返回 "up" 图形时更改(但 Git 并没有真正上升:它实际上只是将提交放入优先级队列,这意味着没有地方可以放置此 name-change 信息)。

如果所有的名称更改都发生在"right"处,--follow--full-history可以混合使用。例如,如果旧名称 a/b/c.ext 的 name-change 到 a/d/c.ext 出现在 M-N 对中,那么 all提交具有旧名称 - 然后 --follow--full-history 在合并的两条腿之后没有问题:该文件只有历史上 M 的一个名称和 --follow 在正确的时间更改一个名称。当 name-change 发生在 平行 历史中,在多个不同的合并分支中,这就搞砸了。