Может ли Git реально отслеживать перемещение одной функции из одного файла в другой? Если так, то как? - PullRequest
63 голосов
/ 05 февраля 2011

Несколько раз я сталкивался с утверждением, что, если вы переместите одну функцию из одного файла в другой, Git сможет отследить ее. Например, эта запись гласит: «Линус говорит, что если вы переместите функцию из одного файла в другой, Git расскажет вам историю этой отдельной функции при перемещении».

Но я немного осведомлен о некоторых конструкциях Git под капотом, и я не понимаю, как это возможно. Так что мне интересно ... это правильное утверждение? И если да, то как это возможно?

Насколько я понимаю, Git хранит содержимое каждого файла в виде BLOB-объекта, и каждый BLOB-объект имеет глобально уникальную идентичность, которая возникает из хэша SHA его содержимого и размера. Затем Git представляет папки как деревья. Любая информация об имени файла принадлежит дереву, а не BLOB-объекту, поэтому переименование файла, например, отображается как изменение дерева, а не BLOB-объекта.

Так что, если у меня есть файл с именем «foo» с 20 функциями в нем и файл с именем «bar» с 5 функциями в нем, и я перемещаю одну из функций из foo в bar (в результате получаем 19 и 6, соответственно), как Git может обнаружить, что я переместил эту функцию из одного файла в другой?

Насколько я понимаю, это приведет к появлению 2 новых BLOB-объектов (один для модифицированного foo и один для модифицированного бара). Я понимаю, что разница может быть рассчитана, чтобы показать, что функция была перемещена из одного файла в другой. Но я не вижу, как история о функции могла бы ассоциироваться с bar вместо foo (во всяком случае, не автоматически).

Если бы Git действительно посмотрел внутри отдельных файлов и вычислил блоб для каждой функции (что было бы сумасшедшим / неосуществимым, потому что вам нужно было бы знать, как анализировать любой возможный язык), тогда я мог видеть, как это возможно.

Итак ... это утверждение правильно или нет? И если это правильно, то чего не хватает в моем понимании?

Ответы [ 5 ]

27 голосов
/ 19 мая 2012

Эта функциональность предоставляется через git blame -C

Опция -C заставляет git пытаться найти совпадения между добавлением или удалением фрагментов текста в проверяемом файле и файлами, измененными в тех же наборах изменений. Дополнительные -CC или -CCC расширяют поиск. Наберите git help blame для справочной страницы.

Попробуйте сами в тестовом репозитории с git blame -C, и вы увидите, что блок кода, который вы только что переместили, возник из исходного файла, к которому он принадлежал.

12 голосов
/ 09 ноября 2017

Начиная с Git 2.15, git diff теперь поддерживает обнаружение перемещенных линий с опцией --color-moved.Это работает для перемещения по файлам.

Работает, очевидно, для цветного вывода терминала.Насколько я могу судить, нет возможности указать ходы в формате простого текстового патча, но это имеет смысл.

Для поведения по умолчанию попробуйте

git diff --color-moved

Команда также принимает параметры, которые в настоящее время no, default, plain, zebra и dimmed_zebra (используйте git help diff, чтобы получить последние параметры и их описания).Например:

git diff --color-moved=zebra

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

5 голосов
/ 06 февраля 2011

Часть этой функции находится в git gui blame (+ имя файла). Он показывает аннотацию строк файла, каждая из которых указывает, когда он был создан и когда последний раз изменялся. Для перемещения кода по файлу он показывает фиксацию исходного файла как создание, а фиксацию, в которой он был добавлен в текущий файл, как последнее изменение . Попробуй.

Что мне действительно нужно, так это дать git log в качестве некоторого аргумента диапазон номеров строк в дополнение к пути к файлу, и тогда будет показана история этого блока кода. Нет такой возможности, если документация верна. Да, из заявления Линуса я тоже думаю, что такая команда должна быть легко доступна.

3 голосов
/ 05 февраля 2011

git на самом деле не отслеживает переименования вообще . Переименование - это просто удаление и добавление, вот и все. Любые инструменты, которые показывают переименования, восстанавливают их из этой информации истории.

Таким образом, функция отслеживания переименований - это простой вопрос анализа различий всех файлов в каждом коммите после факта. В этом нет ничего особенно невозможного; существующее отслеживание переименования уже обрабатывает «нечеткие» переименования, в которых вносятся некоторые изменения в файл, а также в его переименование; это требует просмотра содержимого файлов. Было бы также простым расширением искать переименования функций.

Однако я не знаю, действительно ли базовые инструменты git делают это - они пытаются быть независимыми от языка, а идентификация функций очень не зависит от языка.

2 голосов
/ 05 февраля 2011

Есть git diff, который покажет вам, что определенные строки исчезли из foo и вновь появились в bar.Если в этом же коммите нет других изменений в этих файлах, это будет легко заметить.

Интеллектуальный клиент git сможет показать вам, как строки перемещаются из одного файла в другой.Среда IDE с поддержкой языка сможет сопоставить это изменение с определенной функцией.

Очень похожая ситуация происходит, когда файл переименовывается.Он просто исчезает под одним именем и снова появляется под другим, но любой разумный инструмент может его заметить и представить как переименованный.

...