Git (по крайней мере, с 2.11.1) не отслеживает копии / переименования / перемещения в хранилище. Тем не менее, впоследствии можно сказать, что он анализирует коммиты, но кажется, что это правильно работает только с командами log, а не командами merge. При работе с ветками может потребоваться применить изменения, которые были внесены в исходный файл, ко всем копиям, поэтому будет иметь смысл, что объединение сможет анализировать все фиксации между двумя ревизиями таким же образом, как журнал способен Делать это. К сожалению, я не могу найти правильные параметры / параметры конфигурации, чтобы сделать эту работу.
Смотрите этот пример:
# prepare repository
git init git-copies
cd git-copies
git config --local --add log.follow true
# add initial file
echo first >initial.txt
git add initial.txt
git commit -m 'initial'
# create new branch
git checkout -b copies
# commit first copy
cp initial.txt copy1.txt
git add copy1.txt
git commit -m 'copy 1'
# commit second copy
cp initial.txt copy2.txt
git add copy2.txt
git commit -m 'copy 2'
# rename first copy
mv copy1.txt copy1-renamed.txt
git add copy1-renamed.txt copy1.txt
# at this point git status would show an informational client-local status "renamed" (not recorded in repo AFAIK)
git commit -m 'renamed copy 1'
# create further copies of previously copied files
cp copy1-renamed.txt copy1-renamed-copy1.txt
cp copy1-renamed.txt copy1-renamed-copy2.txt
cp copy2.txt copy2-copy1.txt
cp copy2.txt copy2-copy2.txt
git add copy1-renamed-copy1.txt copy1-renamed-copy2.txt copy2-copy1.txt copy2-copy2.txt
git commit -m 'copies on second level'
# modify each second level second copy
echo 'altered 1' >copy1-renamed-copy2.txt
echo 'altered 2' >copy2-copy2.txt
git add copy1-renamed-copy2.txt copy2-copy2.txt
git commit -m 'altered second level second copies'
# rename the initial file (omit for some extra fun, see below)
mv initial.txt initial-renamed.txt
git add initial.txt initial-renamed.txt
git commit -m 'renamed initial file'
# create a new copy of the renamed initial file (omit for some extra fun, see below)
cp initial-renamed.txt initial-renamed-copy.txt
git add initial-renamed-copy.txt
git commit -m 'copied renamed initial file'
# switch back to master branch and alter initial file
git checkout master
echo changed >initial.txt
git add initial.txt
git commit -m 'changed initial file'
# switch to copies branch again
git checkout copies
Проверьте, какие git распознаются как копии:
for file in *.txt; do echo " -- $file"; git --no-pager log --oneline $file; echo; done
-- copy1-renamed-copy1.txt
71ad85b copies on second level
c9266f9 renamed copy 1
d3a0006 copy 1
a7ff313 initial
-- copy1-renamed-copy2.txt
f5e9a0e altered second level second copies
71ad85b copies on second level
c9266f9 renamed copy 1
d3a0006 copy 1
a7ff313 initial
-- copy1-renamed.txt
c9266f9 renamed copy 1
d3a0006 copy 1
a7ff313 initial
-- copy2-copy1.txt
71ad85b copies on second level
c9266f9 renamed copy 1
d3a0006 copy 1
a7ff313 initial
-- copy2-copy2.txt
f5e9a0e altered second level second copies
71ad85b copies on second level
c9266f9 renamed copy 1
d3a0006 copy 1
a7ff313 initial
-- copy2.txt
f66887f copy 2
d3a0006 copy 1
a7ff313 initial
-- initial-renamed-copy.txt
68a2e29 copied renamed initial file
71ad85b copies on second level
c9266f9 renamed copy 1
d3a0006 copy 1
a7ff313 initial
-- initial-renamed.txt
0d89d9b renamed initial file
a7ff313 initial
Хотя Git не может отличить дословные копии между первым и вторым уровнем от исходного файла (как и ожидалось), он путает initial-renamed-copy.txt
с копией файла второго уровня, но он действительно распознает, что все файлы имеют изначально зафиксированный файл как общий предок. В общем, я ожидаю, что слияние применяет изменения ко всем файлам и вызовет конфликт слияния для копий второго уровня, которые мы изменили позже.
Давайте объединить:
git merge --no-commit master
git status --short
Результат: M copy1-renamed-copy1.txt
А? Это как-то неожиданно ... Давайте сбросим и попробуем еще раз с некоторыми опциями, которые (как я понимаю в документации) должны быть в состоянии помочь:
git reset --hard
git merge --no-commit -s recursive -X patience -X find-renames master
git status --short
Результат: M copy1-renamed-copy1.txt
Эмм ... Давайте попробуем установить для параметров конфигурации renameLimit высокие значения, может быть в этом проблема?
git config --local --add diff.renameLimit 999999
git config --local --add merge.renameLimit 999999
git reset --hard
git merge --no-commit -s recursive -X patience -X find-renames master
git status --short
Результат: M copy1-renamed-copy1.txt
К сожалению, нет ...
Кстати, переименование initial.txt
похоже, сильно запутало Git. Если вы пропустите два коммита, которые я отмечал выше, мы все равно будем применять только изменения к одному файлу, но по крайней мере это изменение будет применено к initial.txt
. Похоже, что он просто выберет один файл случайным образом (но воспроизводимый).
Что я делаю неправильно?