行删除已完成,但无须承担责任

时间:2019-07-19 22:49:04

标签: git git-log git-status

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

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

这里是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

关于这个奥秘有什么想法吗?

1 个答案:

答案 0 :(得分:1)

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

这里有两个问题:

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

  • 第二,当您指定诸如git log(带或不带src/QsGeneralBundle/Repository/EmployeeRepository.php)之类的文件名时,Git会打开 History Simplification 。当简化历史记录功能时,Git只需不必担心一些提交。特别是,它不会遵循合并的内容(错误地-但请参见脚注)不会接受您认为合并进行的更改。

因此,通过强制简化历史记录以保留所有提交(--follow)并强制--full-history将合并提交显示为两个 git log,您应该能够找到(错误地)删除了您想要的提交的位置。


脚注:Git假定每次合并的结果都是正确结果。也就是说,假设合并提交在其右侧传入分支更改时有错误修复,而在其左侧更改时根本没有更改。进一步假设进行合并的人放弃了错误修复程序:

git diff

Git 假定,右侧的更改(错误修复)是错误的介绍,进行合并的人很聪明并且被忽略了,保留 left 端版本(根据进行合并的人,因此根据Git现在也是如此)是“正确的”。实际上,该合并提交是错误 ...,但是当Git进行“历史简化”时,Git认为这是正确的。因此,此时Git仅遵循提交图的左侧。您永远不会看到错误修复!

查看带有修订的提交的唯一方法是避免或禁用“历史记录简化”(省略文件名,或添加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) )。但是由于默认情况下,--full-history不会将合并提交与错误修复提交进行区分,即使那样还不够。


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

Git处理您的计算机坚持使用目录/文件夹的方式(这是允许您的OS提供 empty 的方式)是捆绑您的OS。 Git只是提取文件进行提交,并且如果您的操作系统要求将Git名称为a/d/c.ext的文件创建为文件夹a/b/c.ext中的文件c.ext。然后,在文件夹b中创建一个文件夹a,然后再创建文件夹a,以创建一个名为a/b的文件。如果没有名称以a/b/c.ext开头的 文件,则Git首先不会 create a/b

无论如何,a/b的基本问题在于其实现是一个俗气的黑客。假设历史记录(即存储库中的提交链向后看)看起来像这样,后来的提交向右:

--follow

每个字母代表一个实际的提交哈希。在提交...--I--J \ M--N <-- branch / ...--K--L 中,您有一个名为N的文件;在提交a/d/c.ext中,该文件的名称为Ia/b/c.ext处理此问题的方式是查看每个提交对,一次只看一步:

  • 首先,Git查看git log --follow a/d/c.ext对。

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

    如果不是(如果它仍然是a/d/c.ext中的a/b/c.ext,则当Git比较a/d/c.extM或比较JM时,它将查找L(当前名称,而不是旧名称)。

  • 接下来,Git会查看Ma/d/c.ext对,或者,如果进行“历史简化”并且没有强制 not 进行选择,则选择一对去看,而不是去看另一对,也不会跟随分支的那条腿。

    如果在其中一对中重命名了文件,但在另一对中未重命名,则Git确实无法处理。如果没有J-M,Git将选择一条腿跟随,并在某种程度上假装没有合并,并且重命名检测可以正常工作(尽管我不确定,{{1 }}在合并周围显示)。但是,如果没有在 中对文件进行重命名,那么我们仍然很好:文件仍然具有新名称,因此如果我们强制L-M,则Git现在必须遍历在合并的两条腿上,Git都会这样做。假设它现在执行--full-history部分:

  • Now Git现在比较提交git log--full-history。文件在这里重命名了吗? 假设我们在这里重命名了 。实际上,Git自言自语:哈哈,让我们停止寻找I-J;从现在开始,让我们寻找I 。当Git将提交J与提交a/d/c.ext之前的内容进行比较时,就可以了。

  • 但是,经过一段合并之后,Git现在(无论如何在a/b/c.ext下)可以返回并比较来自<的提交II。合并的另一段。 Git会检查文件--full-history,因为这是它现在认为文件具有的路径。 但是该文件在合并的分支中仍具有K名称。结果是L正在寻找错误的文件!

要使所有这些工作正常进行,a/b/c.ext必须比实际情况更聪明。 a/d/c.ext所做的只是更改要查找的一个文件名,而且它不记得在提交图的某些特定 part 中这样做了。 Git需要以某种方式将名称更改与图遍历相关联,并且每当它返回到图形“上”时就“撤消”更改(但是Git并没有真正上调:它实际上只是将提交放入优先级队列,这意味着没有放置此名称更改信息的地方。

如果所有名称更改都发生在“正确的”位置,则git log --followgit log --follow 可以很好地混合。例如,如果将名称从旧名称--follow更改为--follow是在--full-history对中进行的,那么 all 较旧的提交都具有旧名称—然后,a/d/c.exta/b/c.ext沿合并的两条腿都没有问题:该文件在历史上仅具有M-N中的一个名称,而--follow则更改了一个名称在正确的时间命名。正是在 parallel 历史中,在多个不同的合并分支中发生名称更改时,这才搞砸了。