Подтвердить что ты не робот

Почему git винить не следует за переименованиями?

$ pwd
/data/mdi2/classes

$ git blame -L22,+1 -- utils.js
99b7a802 mdi2/utils.js (user 2015-03-26 21:54:57 +0200 22)  #comment

$ git blame -L22,+1 99b7a802^ -- utils.js
fatal: no such path mdi2/classes/utils.js in 99b7a802^

Как вы заметили, файл находился в другом каталоге в том, что commit

$ git blame -L22,+1 99b7a802^ -- ../utils.js
c5105267 (user 2007-04-10 08:00:20 +0000 22)    #comment 2

Несмотря на doc

The origin of lines is automatically followed across whole-file renames (currently there is no option to turn
       the rename-following off)

винить не следует за переименованиями. Почему?

ОБНОВЛЕНИЕ: Короткий ответ

git blame следует переименовывать, но не для git blame COMMIT^ -- <filename>

Но это слишком сложно для отслеживания переименования файлов вручную через массу переименований и тонны истории. Я думаю, это поведение должно быть исправлено, чтобы тихо следовать переименованиям для git blame COMMIT^ -- <filename>. Или, по крайней мере, --follow должен быть реализован, поэтому я могу: git blame --follow COMMIT^ -- <filename>

UPDATE2: Это невозможно. Читайте ниже.

ОТВЕТ ОТ MAILLIST от Юнио С Хамано

git blame следует переименовывать, но не для git blame COMMIT^ -- <filename>

Предположим, что у вас есть файл A и файл B в версии v1.0.

Шесть месяцев по дороге, код был значительно реорганизован, и вы делаете не нужно содержимое этих двух файлов отдельно. У тебя есть удалены A и B, и большая часть того, что у них есть, теперь находится в файле C. Это текущее состояние.

git blame -C HEAD -- C

может следить за содержимым от обоих просто отлично, но если вы были позволили сказать

git blame v1.0 -- C

что это значит? C вообще не существовало v1.0. Ты прося следовать за содержимым A тогда, или B? Как ты скажите, что вы имели в виду A, а не B, когда вы сказали это C в этой команде?

"git винить" следует за движениями содержимого и никогда не обрабатывает "переименования" в любой особый способ, поскольку глупо, чтобы думать о переименовании как-то особенное; -)

Как вы расскажете, какой контент начать копать в команде из командной строки - дать отправку отправной точки (по умолчанию HEAD, но вы можете дать COMMIT ^ в качестве своего примера) и путь в этом отправная точка. Поскольку нет смысла указывать C на Git и то волшебным образом предположим, что вы имели в виду A в некоторых случаях и B в некоторых Другие. Если у v1.0 не было C, единственная разумная вещь - это выйдите вместо того, чтобы сделать предположение (и не сообщая пользователю, как это догадался).

4b9b3361

Ответ 1

git blame выполняет переименования (как и git log, если вы даете ему --follow). Проблема заключается в том, как он следует за переименованиями, что является не очень тщательным взломом: когда он отбрасывает одну фиксацию за раз (от каждого дочернего элемента до каждого родителя), он делает diff-тот же вид разницы сделайте вручную с помощью

git diff -M SHA1^ SHA1

- и проверяет, обнаружил ли этот diff переименование. 1

Это все прекрасно, насколько это возможно, но это означает, что для git blame для обнаружения переименования (a) git diff -M должен быть способен его обнаружить (к счастью, это имеет место здесь) и вот что вызывая проблемы - он должен перешагнуть через переименование.

Например, предположим, что граф фиксации выглядит примерно так:

A <-- B <-- ... Q <-- R <-- S <-- T

где каждая прописная буква представляет собой фиксацию. Предположим далее, что файл был переименован в commit R, так что в commits R через T он имеет имя newname, тогда как в commit A через Q он имеет имя oldname.

Если вы запустите git blame -- newname, последовательность начинается с T, сравнивает S и T, сравнивает R и S и сравнивает Q и R. Когда он сравнивает Q и R, git blame обнаруживает изменение имени и начинает искать oldname в commits Q и ранее, поэтому, когда он сравнивает P и Q, он сравнивает файлы oldname и oldname в этих двух коммитах.

Если, с другой стороны, вы запускаете git blame R^ -- newname (или git blame Q -- newname), чтобы последовательность начиналась с commit Q, в этом коммите нет файла newname, и при сравнении нет переименования P и Q, а git blame просто отбрасывается.

Фокус в том, что если вы начинаете с фиксации, в которой файл имел предыдущее имя, вы должны указать git старое имя:

git blame R^ -- oldname

а затем все работает снова.


1 В git diff документации вы увидите, что существует опция -M, которая управляет как git diff обнаруживает переименования. Код blame немного модифицирует (и фактически выполняет два прохода, один с -M выключен, а второй с -M включен) и использует свой собственный (другой) -M вариант для несколько разных целей, но в конечном счете он использует этот же код.


[ Изменить, чтобы добавить ответ в комментарий (не вписывался как сам комментарий)]:

Является ли какой-либо инструмент, который может показать мне переименования файлов, например: git переименовать < имя_файла > SHA date oldname- > newname

Не совсем, но git diff -M приближается и может быть достаточно близко.

Я не уверен, что вы подразумеваете под "датой SHA" здесь, но git diff -M позволяет вам поставлять два SHA-1 и сравнивает left-vs-right. Добавьте --name-status, чтобы получить только имена файлов и расположения. Следовательно, git diff -M --name-status HEAD oldsha1 может сообщить, что для преобразования из HEAD в oldsha1, git считает, что вы должны R ename file и сообщать старое имя как "новое" имя. Например, в самом репозитории git имеется файл с именем Documentation/giteveryday.txt, который имел несколько другое имя:

$ git diff -M --name-status HEAD 992cb206
M       .gitignore
M       .mailmap
[...snip...]
M       Documentation/diff-options.txt
R097    Documentation/giteveryday.txt   Documentation/everyday.txt
D       Documentation/everyday.txto
[...]

Если этот файл вам интересен, вы хороши. Вот две проблемы:

  • найти SHA1: откуда взялся 992cb206? Если у вас уже есть SHA-1, это легко; если нет, git rev-list - это инструмент поиска SHA1; прочитайте его документацию;
  • и тот факт, что после серии переименований через каждую фиксацию, совершаемую за один раз, как git blame, может выдавать совершенно разные ответы, чем сравнивать гораздо более позднюю фиксацию (HEAD) с гораздо более ранним фиксацией (992cb206 или что-то еще). В этом случае он выходит так же, но "индекс подобия" здесь составляет 97 из 100. Если бы он был модифицирован намного больше на некоторых промежуточных этапах, индекс подобия мог упасть ниже 50%... Тем не менее, если бы мы немного сравнили версию после 992cb206 до 992cb206 (как было бы git blame), возможно, индекс подобия между этими двумя файлами может быть выше.

Что нужно (и отсутствует) для самого git rev-list для реализации --follow, так что все команды, которые используют git rev-list internal-i.e., большинство команд, которые работают более чем на одной версии, могут сделать трюк. По пути было бы неплохо, если бы оно работало в другом направлении (в настоящее время --follow является только более новым и более старым, т.е. Отлично работает с git blame и работает нормально с git log, пока вы не спрашиваете для самой старой истории сначала с --reverse).