Запись операции копирования файла с помощью Git

144

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

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

Я попытался переместить файл, а затем попытался повторно оформить заказ в исходном месте - после перемещения git не позволит мне проверить исходное расположение.

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

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

Hexdoll
источник
1
Вам следует подумать о том, чтобы принять ответ Роберта. Работает отлично.
EliadL

Ответы:

114

Git не выполняет отслеживание переименований и копий, что означает, что он не записывает переименований или копий. Вместо этого он выполняет переименование и обнаружение копирования . Вы можете запросить обнаружение переименования в git diffgit show), используя -Mопцию, вы можете запросить дополнительное обнаружение копий в измененных файлах, используя -Cопцию ( -Cподразумевает -M), и вы можете запросить более дорогостоящее обнаружение копий среди всех файлов с помощью --find-copies-harderили -C -C( -Cчто подразумевает -M). См. Справочную страницу git-diff .

Вы также можете настроить git так, чтобы он всегда выполнял обнаружение переименования, установив diff.renamesлогическое значение true (например, trueили 1), и вы можете запросить git для обнаружения копирования, установив для него значение copyили copies. См. Справочную страницу git-config .

Также проверьте -lпараметр git diffи соответствующую переменную конфигурации diff.renameLimit.


Обратите внимание, что git log <pathspec>в Git работает по-другому: вот <pathspec>набор разделителей пути, где путь может быть именем (под) каталога. Он фильтрует и упрощает историю, прежде чем в игру вступит переименование и обнаружение копирования. Если вы хотите следить за переименованием и копированием, используйте git log --follow <filename>(который в настоящее время немного ограничен и работает только для одного файла).

Якуб Наребски
источник
2
@allyourcode: Что вас смущает? Чтобы включить обнаружение копирования по умолчанию, вы установите diff.renamesзначение copies(например, « git config diff.renames copies»). Я согласен, что это немного нелогично.
Якуб Наребски
Один раздел, который я не могу разобрать, это «и вы можете попросить сделать по умолчанию также определение переименования». Вы говорите, что есть четыре значения, которые может использовать diff.renames (истина, 1, копировать, копии), и что все они делают одно и то же?
allyourcode
1
@allyourcode: извини, я этого не заметил. Исправлено, спасибо.
Якуб Наребски
4
@ peschü: Git использует объектную базу данных с адресацией по содержанию в качестве хранилища репозитория. Содержимое файла хранится в содержимом blob по адресу, который является хешем содержимого SHA-1 (ну, тип + длина + содержимое). Это означает, что данное содержимое сохраняется только один раз. Nb. эта автоматическая дедупликация была причиной создания системы резервного копирования "bup" с использованием формата git pack.
Якуб Наребски
2
В отличие от решения ниже, это не работает с отслеживанием изменений в диапазоне. Git log допускает аргумент диапазона ( git log -L123,456:file.xyz), который правильно следует за переименованием, но не копирует, и вы не можете передать --follow в этом случае; кроме того, AFAICT, это не работает с git blame.
Клеман,
63

2020-05-19: следующее решение имеет преимущества в том, что не меняет журнал исходного файла, не создает конфликт слияния и он короче.

Вы можете заставить Git определять историю скопированного файла за три коммита:

  • Вместо копирования переключитесь на новую ветку и переместите файл на новое место там.
  • Снова добавьте туда исходный файл.
  • Объедините новую ветку с исходной веткой с опцией без перемотки вперед --no-ff.

(Кредиты принадлежат Раймонду Чену .)


В первом решении было четыре коммита:

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

(Решение взято из https://stackoverflow.com/a/44036771/1389680 .)

Роберт Поллак
источник
8
Простота, краткость, 100% ... Этот ответ - общественная услуга ... голосование за все, что видно
птим
1
Какая разница между moveи rename?
vovan
@vovan Вы имеете в виду тот факт, что в bash вы будете использовать mvобе операции? Я использовал «переместить» для случая, когда может потребоваться изменение каталога файла, и «переименовать» там, где это не так.
Роберт Поллак
4
Я попробовал следовать этому (новому) рецепту, но ничего не вышло. Возможно, вы покажете реальные команды.
Грег Линдал,
3
@RobertPollak Я пробовал разные версии этого, но они не работали. Вы имеете в виду под "переместить файл" git mv orig new? Вы имеете в виду под "прочитал оригинал" cp new orig && git add orig?
ᆼ ᆺ ᆼ