Как ДЕЙСТВИТЕЛЬНО показать журналы переименованных файлов с помощью git?

132

Я относительно новичок в git, раньше использовал Subversion.

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

git log --follow

в командной строке я могу видеть весь журнал переименований.

По словам Линуса Торвальдса, переключатель --follow - это угодник "SVN noob", серьезные пользователи git не используют его:

--follow - это полный взлом, предназначенный просто для удовлетворения бывших пользователей SVN, которые в любом случае ничего не знали о таких вещах, как отцовство или красивые графы ревизий.

Это не совсем фундаментально, но текущая реализация «--follow» на самом деле является средством быстрой предварительной обработки, прикрепленным к логике обхода ревизий, а не чем-то действительно целостным.

Он в буквальном смысле был разработан как приятель «SVN noob», а не как «настоящая функциональность git». Идея заключалась в том, чтобы уйти от (сломанного) мышления, которое переименовывает материю в большой картине.

Мой вопрос : как хардкорные пользователи git среди вас получают историю файла, когда он был переименован? Как это сделать «настоящий»?

Майк
источник
17
@David Hall: git mv oldfile newfileне вызывает переименования должны быть записаны на всех - это точно так же , как удаление одного файла и добавления другого. git только переименовывает и копирует состояние дерева при каждой фиксации постфактум.
Марк Лонгэр
20
@ Дэвид Холл: Если вы переименуете файл с помощью другого инструмента вне git (например /bin/mv oldfile newfile), но затем сделаете это git add newfile; git rm oldfile, результат будет неотличим от результата git mv oldfile newfile.
Марк Лонгэр
1
Эта идеология развалится, если вы когда-нибудь переместите файл в новый репозиторий, и в этом случае неспособность переместить всю его историю может стать серьезной проблемой. Хотя, конечно, есть пределы тому, сколько истинной истории действительно может быть связано с файлом в сложном проекте.
Роман Старков
1
Примечание: git log --followнемного улучшается с git 2.9 (июнь 2016 г.): см. Мой ответ ниже
VonC,
1
Начиная с версии 2.15, вы можете поэкспериментировать, --color-movedкогда вы diff.
Майкл

Ответы:

71

Я думаю, что основная идея, стоящая за точкой зрения Линуса, заключается в том, что - и отнеситесь к этому с долей скепсиса - хардкорных пользователей git никогда не волнует история «файла». Вы помещаете контент в репозиторий git, потому что контент в целом имеет значимую историю.

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

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

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

CB Bailey
источник
Привет, Чарльз, Спасибо за ответ. Кажется, я использую git почти так же, как SVN. Хотя я понимаю, что git сильно отличается от других систем управления версиями, многие концепции в git все же кажутся мне странными ... Я, вероятно, должен закончить ту книгу git, которую недавно купил.
Майк
24
Линус считал, что «правильный» графический интерфейс сможет отслеживать фрагменты кода в файлах, и он надеялся, что к настоящему времени у нас появятся такие инструменты. К сожалению, у нас все еще нет такой роскоши, и --follow по-прежнему полезен.
Майкл Паркер
11
Действительно ли git предлагает другое решение, кроме --followэтого?
Griwes 09
13
Я бы сказал, что мышление «целое дерево» усиливается, если --followоно используется по умолчанию. Я имею в виду, что когда я хочу увидеть историю кода в файле, меня обычно не волнует, был ли файл переименован или нет, я просто хочу увидеть историю кода, независимо от переименований. Так что, на мой взгляд, имеет смысл --followиспользовать значение по умолчанию, потому что меня не интересуют отдельные файлы; --followпомогает мне игнорировать переименования отдельных файлов, которые обычно не имеют большого значения.
Изменено
2
Итак ... если я решу заботиться о «содержимом», а не о файле, как мне распечатать коммиты, относящиеся к содержимому, которое в настоящее время находится в этом файле? Я был бы рад, если бы git отслеживал все это в разных файлах для меня и сообщал журнал всех изменений - я просто не знаю, как это получить.
Эд Авис
36

У меня точно такая же проблема, с которой столкнулись вы. Несмотря на то, что я не могу вам ответить, я думаю, вы можете прочитать это письмо, которое Линус написал еще в 2005 году, оно очень уместно и может дать вам подсказку о том, как справиться с проблемой:

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

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

Я нашел ссылку на это сообщение в блоге, которое также может быть полезно для вас, чтобы найти жизнеспособное решение:

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

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

  1. Коммит действительно представил блок кода. Автор коммита был изобретателем той классной функции, за которой вы искали ее происхождение (или виновной стороной, которая внесла ошибку); или
  2. Блок кода не существовал в файле, но пять его идентичных копий существовали в разных файлах, и все они исчезли после фиксации. Автор коммита реорганизовал дублированный код, добавив единственную вспомогательную функцию; или
  3. (как особый случай) Перед фиксацией файл, который в настоящее время содержит блок кода, который вас интересует, сам по себе не существовал, но существовал другой файл с почти идентичным содержимым, и блок интересующего вас кода, вместе со всем остальным содержимым файла, существовавшим тогда, действительно существующим в этом другом файле. Он исчез после фиксации. Автор коммита переименовал файл, внося в него незначительные изменения.

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

Пожалуйста, держите нас в курсе вашего прогресса в этом вопросе.

Альберто Бачелли
источник
Спасибо за размещение этих статей. Только когда я их прочитал, я полностью понял идею истории контента! Я думал об этом неправильно!
DavidG
Это письмо от Линуса отличное, спасибо, что разместили его.
mik01aj
Интересный--color-moved факт, Git v2.15 добавляет шаг к этой «идеальной системе отслеживания». Я играл с ним, чтобы увидеть, как он отслеживает перемещенные строки в файле, но случайно понял, что он отслеживает перемещенные строки во всем различии.
Майкл
2
Линус объясняет сложную ситуацию. Но здесь у нас простая ситуация: файл просто переименовали (или переместили в другой каталог). Таким образом, должно быть простое решение. Я думаю, что проблема связана с тем фактом, что, в отличие от Subversion, пользователь не может указать Git во время фиксации, откуда берется файл, и это --followможет быть неверно (например, если 2 файла имеют одинаковое содержимое или если есть модификация в дополнение к перемещению файла).
vinc17
13

Я заметил, что большинство графических интерфейсов git и плагинов IDE, похоже, не могут отображать историю файла, если файл был переименован.

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

  • SourceTree при просмотре журнала файлов имеет флажок "Следить за переименованными файлами" в левом нижнем углу.
  • TortoiseGit имеет флажок "следовать переименованию" в окне журнала в левом нижнем углу.

Дополнительная информация об инструментах Git UI:

Майкл Паркер
источник
Источник отлично работает при однократном переименовании, при двукратном переименовании детали изменения недоступны для коммитов перед переименованием. Я сообщил об ошибке здесь: jira.atlassian.com/browse/SRCTREE-5715
Intel
gitk отлично работает, даже когда файл дважды переименовывается в истории. Команда выглядит так: «gitk --follow path / to / file»
Intel
6

Примечание: git 2.9 (июнь 2016 г.) немного улучшит "глючный" характер git log --follow:

См. Commit ca4e3ca (30 марта 2016 г.) Седера Габора ( szeder) .
(Объединено Junio ​​C Hamano - gitster- в коммите 26effb8 , 13 апреля 2016 г.)

diffcore: исправить порядок итерации идентичных файлов при обнаружении переименования

Если два пути ' dir/A/file' и ' dir/B/file' имеют идентичное содержимое и родительский каталог переименован, например, ' git mv dir other-dir', то diffcoreсообщает о следующих точных переименованиях:

renamed:    dir/B/file -> other-dir/A/file
renamed:    dir/A/file -> other-dir/B/file

(обратите внимание на инверсию здесь:, B/file -> A/fileи A/file -> B/file)

Хотя технически это нормально, это сбивает с толку не только пользователя, но и команды git, которые принимают решения на основе информации о переименовании, например, git log --follow other-dir/A/file«следует dir/B/file» после переименования.

Такое поведение является побочным эффектом фиксации v2.0.0-rc4 ~ 8 ^ 2 ~ 14 ( diffcore-rename.c: упрощение поиска точных переименований, 2013-11-14): хэш-карта, хранящая источники, возвращает записи из того же сегмента, то есть источники, соответствующие текущему месту назначения , в порядке LIFO.
Таким образом, итерация сначала проверяет ' other-dir/A/file' и ' dir/B/file' и, обнаружив идентичное содержимое и базовое имя, сообщает точное переименование.

VonC
источник
2

В Linux я убедился, что SmartGit и GitEye могут отслеживать переименования при отслеживании истории конкретного файла. Однако, в отличие от gitk и GitEye, SmartGit показывает отдельное представление файла и представление репозитория (которое содержит структуру каталогов, но не список файлов, содержащихся внутри).

prusswan
источник