Некоторые строки были удалены из файла, и я хотел бы знать, когда и как это произошло.
Но: ошибочных коммитов не обнаружено. Действительно странно.
Вот git log
, который показывает (последние изменения) фиксацию при добавлении строк:
$ git log --follow -p -- src/QsGeneralBundle/Repository/EmployeeRepository.php
commit 80dc439aba2bf38ed7e95888dd21c1ba0f8960ce
Author: Benoit <[email protected]>
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 включает Упрощение истории. Когда упрощение истории включено, Git просто не мешает посмотреть при некоторых коммитах. В частности, он не будет следовать за этапом слияния, которое (ошибочно, но см. сноску) не брать является изменением, которое, по вашему мнению, вносит слияние должен.
Следовательно, заставив упрощение истории сохранить все коммиты (--full-history
) и заставив git log
показывать коммиты слияния как дваgit diff
s, вы сможете найти, где нужный коммит был (неправильно) удален.
Примечание: 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)
Git предполагает, что правые изменения — исправление ошибки — были вместо этого введение ошибки, которую человек, который сделал слияние, был умен и пропущен, сохраняя версию осталось на стороне, которая (по словам человека, выполняющего слияние, и следовательно, теперь, согласно Git, тоже) было «правильным». Фактически, этот коммит слияния был неправильно... но когда Git делает упрощение истории, Git предполагает, что это было правильно. Таким образом, на данный момент Git следует только левой части графа коммитов. Вы никогда не увидите исправление ошибки!
Единственный способ увидеть фиксацию с исправлением — избежать или отключить упрощение истории (пропустить имя файла или добавить --full-history
). Но поскольку по умолчанию git log -p
не будет отличать фиксацию слияния от фиксации исправления ошибки, даже этого недостаточно.
Дополнительная сноска: обратите внимание, что имена файлов в Git — это весь путь. Если файл назывался a/b/c.ext
, а теперь называется a/d/c.ext
, этот файл имеет два разных имени. Git не хранит каталоги/папки, что почему нельзя хранить пустой каталог. (Примечание к сноске: в каждом репозитории есть является пустой объект дерева, но его использование позже приводит к проблемам, поскольку Git продолжает пытаться преобразовать его в подмодуль — подробности см. в связанном вопросе.)
То, как Git справляется с настойчивостью вашего компьютера в использовании каталогов/папок, что позволяет вашей ОС предоставлять пустой, заключается в том, чтобы нянчиться с вашей ОС. Git просто извлекает файлы для фиксации, и если ваша ОС требует, чтобы файл с именем Git a/b/c.ext
был создан как файл c.ext
в папке b
в папке a
, тогда Git создает папку a
, а затем папку a/b
, чтобы создайте файл с именем a/b/c.ext
. Если есть файлы нет, имя которых начинается с a/b
, Git просто не Создайтеa/b
в первую очередь.
В любом случае, фундаментальная проблема с --follow
заключается в том, что его реализация — дрянной хак. Предположим, что история — т. е. обратная цепочка коммитов в репозитории — выглядит примерно так, с последующими коммитами справа:
...--I--J
\
M--N <-- branch
/
...--K--L
где каждая буква соответствует фактическому хэшу коммита. В коммите N
у вас есть файл с именем a/d/c.ext
; в коммите I
этот же файл имеет имя a/b/c.ext
. способ, который git log --follow a/d/c.ext
обрабатывает это, должен просматривать каждую пару коммитов, шаг за шагом:
Сначала Git смотрит на пару M-N
.
Находится ли файл с именем a/b/c.ext
в M
и a/d/c.ext
в N
? Если это так, то, как только Git обработает пары J-M
и L-M
, Git остановка будет искать a/d/c.ext
и вместо этого начнет искать a/b/c.ext
(старое имя, а не текущее).
Если нет — если это все еще a/d/c.ext
в M
— тогда, когда Git сравнивает J
и M
или сравнивает L
и M
, он будет искать a/d/c.ext
(текущее имя, а не старое имя).
Затем Git просматривает либо пару J-M
, либо L-M
— или, если выполняется упрощение истории, а не принудительно нет, выбирает одну из пар для просмотра и на самом деле не смотрит на другую пару, а также не следует за этой частью ветви. .
Если файл переименовывается в одной из этих пар, но не в другой, Git действительно не может с этим справиться. Без --full-history
Git выберет одну ногу, чтобы следовать, и в какой-то степени притворится, что слияния нет, и обнаружение переименования может работать (хотя я не уверен, что это так, учитывая другие странности, которые git log
отображает вокруг слияний). Однако, если файл не переименован в пару либо, все в порядке: файл по-прежнему имеет свое новое имя, поэтому, если мы принудительно --full-history
, чтобы Git теперь должен был пройти обе части слияния, Git сделает это. Допустим, теперь он выполняет часть I-J
:
Теперь Git сравнивает коммиты I
и J
. Файл был переименован здесь? Допустим, это было, переименованное сюда. По сути, Git говорит себе: Ага, давайте перестанем искать a/d/c.ext
; с этого момента давайте искать a/b/c.ext
. Когда Git сравнивает фиксацию I
с тем, что предшествует фиксации I
, это работает.
Но, проследив какое-то время за этой частью слияния, Git теперь — в любом случае под --full-history
— возвращается и сравнивает коммиты K
и L
из части разное слияния. Git проверяет файл a/b/c.ext
, так как теперь он считает, что это путь к файлу. Но файл по-прежнему имеет имя a/d/c.ext
в это ветке слияния. В результате git log --follow
ищет неправильный файл!
Чтобы все это работало, git log --follow
нужно быть способ умнее, чем есть на самом деле. Все, что делает --follow
, — это изменяет одно имя файла, который он ищет, и не помнит, что он сделал это в каком-то конкретном часть графа фиксации. Git должен каким-то образом связать изменение имени с обходом графа и «отменять» изменение всякий раз, когда оно возвращается «вверх» по графу (но Git на самом деле не поднимается вверх: на самом деле он просто помещает коммиты в приоритетную очередь, что означает нет места для размещения этой информации об изменении имени).
Если все изменения имени происходят в «правильных» местах, --follow
и --full-history
могу хорошо сочетаются. Например, если изменение имени на a/d/c.ext
из старого имени a/b/c.ext
происходит в паре M-N
, так что все более старые коммиты имеют старое имя — тогда --follow
и --full-history
не возникнет проблем после обеих частей слияния: файл имеет только одно имя из M
в истории, а --follow
меняет одно имя в нужное время. Это происходит, когда изменение имени происходит в истории параллельно, в нескольких разных ветках слияния.
В любом случае, спасибо, добавление --full-history -m
действительно показывает мне, что виновато слияние. Git сделал это автоматически: / могу ли я доверять автоматическим слияниям git? Или я всегда должен все перепроверять?
То же самое было сделано для многих файлов
Git не хранит каталоги, Git хранит только файлы. Если имя файла когда-то было a/b/c
, а теперь a/d/c
, файл был переименован: имя файла представляет собой полную строку. Это не каталог, это просто часть имени файла!
Что касается того, что произошло при слиянии, вы можете повторить слияния, чтобы увидеть, действительно ли это сделал Git; и/или вы можете просмотреть два отличия от трех коммитов, чтобы увидеть, что увидел Git, что может заставить Git сделать это. Могу поспорить, что Git объявил конфликт во время слияния, и кто-то использовал -X
или -s
, чтобы «исправить» конфликт, проигнорировав одну сторону слияния (или сделал то же самое в инструменте слияния).
Как ни странно, он никогда не переименовывался (только родительский подкаталог много лет назад), но
git log
даже не включает эту фиксацию последних изменений, в то время какgit log --follow
включает...