Как рассчитывается индекс сходства git? - PullRequest
0 голосов
/ 05 июля 2018

Я новичок в git, и есть кое-что, что мне не понятно. Как внутри Git узнает, является ли файл новым или измененным файлом?
Поскольку git не отслеживает файлы, а отслеживает BLOB-объекты. Это связано с индексом сходства?

Также я столкнулся с проблемой, что при перемещении файла и его изменении, git иногда распознает его как переименованный файл, а иногда как новый файл.
В случае небольшого файла он распознает его как новый файл и удалит.

Как я могу "обмануть" git, чтобы пометить этот случай как перемещенный файл, а не как новый и удаленный (без выполнения двух разных коммитов - один для перемещения, другой для изменений)?

1 Ответ

0 голосов
/ 05 июля 2018

Подробное обсуждение вычисления индекса подобия см. В Попытка понять `git diff` и` git mv` механизм обнаружения переименования . Однако прежде чем сделать это, примите к сведению:

  • Каждый коммит представляет собой полный автономный снимок. Снимок - это дерево именованных файлов и именованных каталогов (или папок), содержащее больше файлов и / или более каталогов. 1 При наличии коммита и полного имени пути path/to/file.ext, Git может извлечь соответствующие содержимое BLOB-объекта (как их называет Git), в котором содержится именованный файл внутри этого коммита, без необходимости просматривать другие коммиты.

  • Каждый раз, когда вы спрашиваете Git о снимке для целей сравнения, вы должны указать Git хэш-идентификаторы или имена или другие строки, которые разрешаются в хэш-идентификаторы, из двух коммитов - двух снимков. Git в действительности извлекает каждый снимок по одному, а затем сравнивает результирующее дерево файлов. (Некоторые команды, такие как git show и git log -p, выясняют родительский хеш, просматривая дочерний коммит, затем сравнивают родительский и дочерний в этом порядке.)

Таким образом, Git всегда смотрит на пару деревьев: левое (a/) дерево может содержать README.txt, а правое (b/) также содержит Например, README.txt, в то время как левая сторона содержит doc.txt, а правая не имеет doc.txt. Левый коммит не имеет documentation.rst, а правый имеет documentation.rst.

На этом этапе Git сопоставляет файлов. Два файла с одинаковым именем пути (например, два файла README.txt здесь) должны быть "одним и тем же" файлом, поэтому Git просматривает содержимое левой стороны README.txt и содержимое правой стороны README.txt произвести различие этих двух. Технический термин для сопоставления таких вещей определяет идентичность файлов. (Это настоящий подвиг с философской точки зрения. См. Корабль Фесуса для обсуждения. В отличие от философских рассуждений, в вычислительной технике мы получаем четкий и конкретный ответ. Что ж, мы будем поступать, пока не введем такие вещи, как Git's -B или break значение в git diff, как минимум!)

Если имена не совпадают, например, doc.txt против documentation.rst, Git вычисляет индекс сходства между каждой такой парой файлов, сравнивая файлы левой стороны (которые в этот момент кажется, что удаляются при переходе на правую сторону) к файлам правой стороны (которые сейчас кажутся новыми файлами). То есть Git вычисляет этот индекс , если вы включили обнаружение переименования . Обнаружение переименования отключено по умолчанию в версиях Git до Git версии 2.9 и включено по умолчанию в последующих версиях. Git выбирает здесь лучшие совпадения и объединяет файлы в пару: если doc.txt достаточно похож на documentation.rst, почему тогда это также должен быть "один и тот же" файл, даже если они имеют разные имена.

Прежде чем Git даже надумает использовать этот трюк с индексом сходства, он делает первый проход, чтобы найти 100% идентичные файлы. Это намного проще, чем вычисление индекса сходства, благодаря тому, как Git хранит контент. Любые такие точные совпадения объединяются в пару и исключаются из списка файлов, которые потенциально могут быть сопоставлены, оставляя только те файлы, которые не имеют точные совпадения в том, что Git внутренне называет очередь переименования. Таким образом, вычисление индекса сходства выполняется только для файлов, имена которых находятся в очереди переименования. Это вычисление относительно дорого (его число равно O (n 2 ) по количеству файлов), поэтому для быстрых git show или git log -p хорошей идеей будет зафиксировать только переименованием сначала, а затем любые изменения в содержании.


1 Это внутреннее представление - снаружи вы даже не должны знать или заботиться о том, чтобы Git сохранял каждый каталог в виде записи дерева. В частности, Git любит утверждать, что он хранит только files (не каталоги), а Git до смешного затрудняет хранение пустой директории. Для этого у Git должно быть пустое дерево - и оно делает , но если вы попытаетесь его использовать, вы получите странные эффекты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...