Git: Изменить свойство Modification файла - PullRequest
0 голосов
/ 10 октября 2018

Наш код использует базу данных, конкретная версия схемы этого БД связана с номером версии.Допустим, текущая версия - 95.

Каждый раз, когда ветвь функций разработчика изменяет схему БД, он / она создает соответствующий файл обновления.Теперь, если я изменю схему в своей ветке, я создам файл update_96.Но если кто-то еще одновременно обновляет базу данных и его / его ветвь функций объединяется для разработки ранее, файл с этим именем уже присутствует.Слияние или перебазирование может привести к конфликту.

Теперь два новых update_96 файла независимы: я подумал, что мне просто нужно переименовать мой в update_97, и перебазировка будет в порядке.Но git «знает», что мой update_97 был раньше update_96, и в любом случае пытается объединить эти два файла.Я думаю, причина в том, что свойство Modification Renamed-Modified.

Как изменить это свойство на Added?Различные эксперименты с удалением / чтением update_97 в отдельных коммитах не решили проблему.

Ответы [ 2 ]

0 голосов
/ 10 октября 2018

Git не имеет свойств файла.Каждый коммит просто хранит снимок файлов.Если в коммите A есть файл X, а в коммите B - файл Y, это два отдельных коммита с двумя отдельными файлами.

Когда Git сравнивает два коммита, это все равно, что сравнивать два изображения в одном из них. Найди отличия игры .Снимок слева и снимок справа.Каждый снимок состоит из файлов.Итак, давайте поместим коммит A слева и коммит B справа, и сравним:

  • Если коммит A имеет X и Y, а коммит B имеет только Y, тогда результат сравнения коммита A с коммитом B будет: файл X был удален (плюс все, что отличается от Y).

  • ЕслиДля коммита A есть только X, а для коммита B - X и Y, тогда результат сравнения A с B будет следующим: файл Y был создан (наряду со всем, что отличаетсяв X).

  • Но если в коммите А есть X (а не Y), а в коммите В - Y (а не X), сейчас все становится интереснее.Есть пара файлов, в которых имена не совпадают, но, возможно, содержимое все еще соответствует .

Это третий случай, когда Git попытается угадать , был ли файл переименован (и, возможно, также изменен).Git увидит файл переименованным (и в остальном неизменным), если содержимое точно соответствует, бит за битом, идентично на 100%.Но этого недостаточно для реальных случаев, поэтому Git также увидит файлы, переименованные, если содержимое просто соответствует примерно 99%, или 80%, или даже 51%.

Это переименование-обнаружение - это вопрос определения личности файла.Файл X в A такой же, как файл Y в B, или нет?В общем, это очень сложная проблема (см. Философскую проблему Корабля Тесея ).У Git есть конкретный ответ: он вычисляет индекс сходства , число от 0% схожего до 100% с точным соответствием и определяет, что файл переименован - то есть тот же файл- если несопоставленный файл левой стороны, по крайней мере, на 50% похож на непревзойденный файл правой стороны.Файлы с совпадениями имен уже спарены как «тот же файл», так что это применимо только к случаям, подобным приведенным выше, с файлами X и Y.

Этот «50% аналог» является просто по умолчанию , хотя.Если ваша миграция, скажем, на 62% похожа на миграцию другого парня, в то время как все другие файлы, которые действительно должны быть обнаружены как "переименованные", более похожи, вы можете поднять порог .(С помощью git diff вы также можете полностью отключить обнаружение переименования. На самом деле, в более старых версиях Git по умолчанию отключено, и должно быть включено с помощью -M. В Git 2.9 значение по умолчанию переключено на «включено». * * * * * * * * * * * * * *

* * * * * * * * * * * * * * * git diff * *.Но git rebase на самом деле представляет собой серию операций выбора вишни, и каждый выбор вишни является слиянием, в то время как git merge также, конечно, является слиянием (ну, за исключением случаев, когда это «ускоренная перемотка вперед», то естьне слияние вообще).Следовательно, все, что вы делаете, включает внутренний механизм слияния Git. Это означает, что вам нужно понять , как работает механизм слияния Git, что на самом деле довольно просто:
  • Операция слияния имеет три входа: база слияния - вызовите ее B - и левую сторону L и правую сторону R .Левая сторона L - это фиксация --ours, а R - фиксация --theirs.

  • Для выполненияGit сравнивает базу слияния с каждой из двух сторон:

    git diff --find-renames <B> <L>   # what we changed
    git diff --find-renames <B> <R>   # what they changed
    

    GiЗатем объединяет два набора изменений в одно большое изменение, которое применяется к B .Конфликты слияния возникают, когда два набора изменений касаются идентичных файлов в идентичных местах - что, конечно, требует решения философской проблемы «Корабль Тесея», для которого это одни и те же файлы, но для этого предназначен --find-renames выше.

    (И, конечно, если Git находит переименования, вы можете иметь конфликты переименования / переименования или конфликты переименования / удаления. Но вам не нужно беспокоиться об этом, пока это не произойдет.Если только одна «сторона» этих двух различий - одна B-vs - что угодно - переименовывает файл, Git объединяет изменения, принимая переименование.)

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

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

К счастью, как и в случае git diff,Вы можете контролировать порог сходства.Команды git merge и git rebase принимают то, что Git называет опции стратегии-опции , что, на мой взгляд, ужасное имя;Я называю их расширенными опциями , так как письмо для них - -X (расширенное).Одним из них является -X find-renames=<em>threshold</em>.Таким образом, если все хорошие переименования похожи как минимум на 90%, а bad переименованы только на 62%, то:

git merge ... -X find-renames=90 ...

остановит Gitот обнаружения «неправильного» переименования.

Конечно, если какое-то «хорошее» переименование находится на 51%, а какое-то «плохое» переименование на 99%, это не поможет.И, не зная, каковы действительные числа, как вы знаете, что использовать для -X find-renames?Ответ заключается в том, чтобы сделать эти git diff самостоятельно:

  • Найти базу слияния.Для git merge используйте git merge-base --all.Для cherry-pick база слияния является родителем копируемого коммита.Rebase использует cherry-pick, поэтому база слияния для каждого копируемого коммита является родителем этого коммита.Найдите коммит-пару, в которой происходит «переименование» (на самом деле это не переименование), и используйте ее родительский элемент.

  • Теперь, когда у вас есть база слияния, сравните ее с интереснойфиксирует (левая и правая стороны слияния), используя git diff.Git скажет вам, что такое индекс сходства для коммитов, где было обнаружено переименование.(Если у вас более старая версия Git, обязательно используйте -M50 или установите diff.renames на true, чтобы git diff обнаружил переименование.)

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

Есть еще некоторые проблемы.В частности, если вы делаете ребаз, это делает целую серию черриков.--ours или левая сторона каждого слияния - это коммит, на который выбирается вишня, и вы не знаете, что это будет, пока не закончите копировать предыдущий коммит, потому что rebase создает new коммит цепочка по одному коммиту за раз.То есть ввод L commit является выводом из предыдущего cherry-pick.(Вот почему некоторые ребазы снова и снова повторяют одни и те же конфликты слияний.)

Тем не менее, это общий метод, с помощью которого вы можете решить каждую проблему, используя инструменты, предоставляемые Git.(На практике часто можно просто догадаться: плохое совпадение означает, что значение по умолчанию 50% слишком приемлемо, поэтому просто перейдите на полпути к строгому, или 75%, и повторите попытку. Пропущенное совпадение означает, что оно слишком строго, поэтому перейдите на полпути к разрешающему,или 25%, и повторите попытку.)

(Обратите внимание, что -X find-renames - это новое правописание; в очень старых версиях Git это -X rename-threshold. Проверьте локальную документацию git merge.)

0 голосов
/ 10 октября 2018

Вы можете установить атрибут git -merge для этих файлов, что вообще предотвратит автоматическое слияние.

Чтобы сделать это, вы должны добавить строку типа

update_* -merge

на ваш .gitattributes

См .: https://git -scm.com / docs / gitattributes # _performing_a_three_way_merge

Более сложное решение определит и установит пользовательский драйвер слияния (также описано в приведенной выше ссылке)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...