Попытка понять `git diff` и`git mv` механизм обнаружения переименования
Это продолжение другого вопроса, который я задавал раньше.
Перед редактированием изначально созданный файл something
переименовывается в somethingelse
что можно наблюдать здесь:
git mv something somethingelse
Файл somethingelse
затем переименовывается обратно в something
перед вторым редактированием vim:
git mv somethingelse something
В основном в следующей части кода:
# If you add something to the first line, the rename will not be detected by Git
# However, if you instead create 2 newlines and fill line 3 with new code,
# the rename gets detected for whatever reason
printf "\nCOMMAND: vim something\n\n"
vim something
Если в этот момент я добавлю abc
к коду, мы бы в конечном итоге:
First line of code. abc
Я думаю, что это добавление 4 байтов в строке 1, что, в свою очередь, приведет к следующему:
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: something
deleted: somethingelse
Затем, если мы добавим новую строку и введем abc в третью строку (которая также должна быть 4 байта, исправьте меня, если ошибаюсь):
First line of code.
abc
Внезапно Git обнаружит переименование (включая редактирование):
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: somethingelse -> something
Один хороший ответ / комментарий @torek, данный здесь, объясняет это в определенной степени, принимая git diff
переименовать порог обнаружения git status
во внимание.
Разве Git не должен вести себя одинаково, так как мы добавили 4 байта в обоих случаях, но по-разному, или новая строка имеет какое-то отношение к этому?
1 ответ
Насколько мне известно, вычисление Git "индекса сходства" не документировано нигде, кроме как в исходном коде, начиная с diffcore-delta.c.
Чтобы вычислить индекс сходства для двух файлов S (источник) и D (место назначения), Git:
- читает оба файла
- вычисляет хеш-таблицу всех кусков файла S
- вычисляет вторую хеш-таблицу всех кусков файла D
Записи в этих двух хеш-таблицах - это просто количество вхождений экземпляров этого хеш-значения (плюс, как отмечено ниже, длина фрагмента).
Значение хеш-функции для файлового блока вычисляется по формуле:
- начать с текущего смещения файла (изначально ноль)
- читать 64 байта или до
'\n'
характер, в зависимости от того, что произойдет первым - если файл считается текстовым и существует
'\r'
перед'\n'
Откажитесь от'\r'
- хэшируйте полученную строку размером до 64 байтов, используя алгоритм, показанный в связанном файле
Теперь, когда есть хеш-таблицы для S и D, каждый возможный хеш hi появляется nS раз в S и nD в D (любой из них может быть нулевым, хотя код пропускает сразу оба значения хеша с нулевым значением). Если количество вхождений в D меньше или равно количеству вхождений в S - т.е. nD ≤ nS - то D "копирует из S" nD раз. Если число вхождений в D превышает число в S (в том числе, когда число в S равно нулю), то D имеет "буквальное добавление" из nD - nS вхождений хешированного фрагмента, и D также копирует все nS оригинальные происшествия, а также.
Каждый хеш-блок сохраняет количество входных байтов, и они умножают количество копий или количество добавлений "кусков", чтобы получить количество скопированных или добавленных байтов. (Удаления, где в D отсутствуют элементы, существующие в S, здесь имеют только косвенный эффект: количество копий и добавлений в байтах уменьшается, но Git специально не считает сами удаления.)
Эти два значения (src_copied
а также literal_added
) вычислено в diffcore_count_changes
переданы в эксплуатацию estimate_similarity
вdiffcore-rename.c
, Это полностью игнорирует literal_added
count (этот счетчик используется при принятии решения о том, как создавать дельты пакетов, но не с точки зрения оценки переименования). Вместо этого только src_copied
номер имеет значение:
score = (int)(src_copied * MAX_SCORE / max_size);
где max_size
это размер в байтах большего из двух входных файлов S и D.
Обратите внимание, что есть более ранние вычисления:
max_size = ((src->size > dst->size) ? src->size : dst->size);
base_size = ((src->size < dst->size) ? src->size : dst->size);
delta_size = max_size - base_size;
и если два файла изменили размер "слишком сильно":
if (max_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
return 0;
мы никогда не попадаем в diffcore-delta.c
код для их хеширования. minimum_score
вот аргумент -M
или же --find-renames
, преобразованный в масштабированное число. MAX_SCORE
является 60000.0
(тип double
), поэтому по умолчанию minimum_score
, когда вы используете по умолчанию -M50%
составляет 30000 (половина из 60000). Однако, за исключением случая приема пищи CR-before-LF, этот конкретный ярлык не должен влиять на результат более дорогого вычисления подобия.
Снова, git status
всегда использует значение по умолчанию. Нет ручки для изменения порога (ни количества файлов, разрешенных в очереди поиска переименования). Если бы здесь был код, перейдите сюда, установив rename_score
поле параметров сравнения.