Git объединить один файл .... и ИСТИННОЕ 3-СТОРОННЕЕ СЛИЯНИЕ, а не просто проверку наших и их, которая не учитывает общего предка

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

В SO и в блогах распространяется много дезинформации об использовании git checkout --ours или git checkout --theirs для слияния одного файла. Это будет эффективно выполнять только простой патч, выбирая тот или иной. Это не настоящее трехстороннее слияние с использованием общего предка в качестве базового файла! Неконфликтующие изменения будут потеряны с не выбранной стороны!

У меня есть файл конфигурации (config.toml) в корне моего репозитория, который необходимо автоматически разрешить, выбрав «их» (или иногда «наш», в зависимости от другого условия). Начиная с «основной» ветки, эта первая запись становится общей фиксацией.

path = "some/path"
description = "this is a description"
owner = "username"
foo = "bar"
log = "some stuff"

Затем я проверяю новую ветку, скажем, «новая ветка», вне этой фиксации, добавляю одну строку и меняю строку журнала.

path = "some/path"
description = "this is a description"
owner = "username"
new = "line"
foo = "bar"
log = "new branch"

Затем я возвращаюсь к «основной» ветке, с которой начал, и меняю только строку журнала.

path = "some/path"
description = "this is a description"
owner = "username"
foo = "bar"
log = "main updates"

Я возвращаюсь к ветке 'newbranch'. Если я сделаю git merge -Xtheirs main, все будет работать так, как вы ожидаете.

path = "some/path"
description = "this is a description"
owner = "username"
new = "line"
foo = "bar"
log = "main updates"

Единственным настоящим конфликтом была строка журнала, которая была автоматически разрешена путем выбора «их» (тот, что из «основной» ветки). Добавленная строка new = "line" сохраняется, как и должно быть, учитывая знание коммита общего предка, где разошлись ветки.

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

Общий ответ на эту проблему заключается в использовании

git checkout --theirs main -- config.toml

На самом деле это не выполняет трехстороннее слияние, а просто выбирает свою версию файла. А добавленный new = "line" удаляется. Сходным образом,

git checkout --patch main -- config.toml также не использует знание коммита общего предка и снова рассматривает запись new = "line" как нечто, что следует удалить.

Я видел .gitattributes, предложенный в качестве решения для предоставления стратегий слияния файлов, но мне не повезло заставить это действительно работать. И, казалось бы, немного жестковато, нет возможности выбрать, хочу ли я «их» или «наших» в момент слияния.

Есть ли способ выполнить настоящее трехстороннее слияние в одном файле, учитывающем общего предка? Я несколько шокирован тем, что нет более очевидного ответа на этот вопрос. Благодарю вас!

ОБНОВЛЕНИЕ: я полагаю, что одним из решений было бы проверить общего предка, «основную» версию файла и «новую ветку» во временный каталог. Затем вручную выполните трехстороннее слияние, используя «diff3», перезаписав версию «newbranch», прежде чем совершать это.

ОБНОВЛЕНИЕ 2: не появляется, я действительно могу автоматически разрешить использование «diff3», так что это действительно нежизнеспособный вариант. Кажется, проще всего сначала git merge -Xtheirs --no-commit main скопировать отдельные файлы, которые я хотел объединить git merge -Xtheirs, в каталог tmp. git merge --abort отступить. git merge --no-commit main сделать слияние. Естественно будут конфликты с этими файлами. Скопируйте эти отдельные файлы обратно в репозиторий. git add <those_files>. До этого момента я делал это полностью программно. Если есть другие файлы, которые необходимо разрешить, они будут выполнены пользователем вручную. В противном случае git commit -m "merged 'main' into 'newbranch' завершить слияние.

Какова ваша конечная цель, для которой требуется эта настраиваемая стратегия слияния для каждого файла? Я не верю, что Git предлагает способ сделать то, что вы хотите, но если вы объясните свою конечную цель, мы сможем предложить способ ее достижения.

bk2204 18.03.2022 23:49

конечная цель определена там. То есть сделать настоящее слияние, так же, как git обычно обрабатывает это, предоставляя «git merge -X их» или «git merge -X наши», но для каждого файла. По сути, автоматически разрешать истинные конфликтующие строки либо с «их», либо с «нашими», не просто выбирая весь файл из «их» или «наш», тем самым сохраняя неконфликтующие дополнения, которые были сделаны в файле в любой из ветвей (например, « new = "line"' приведенный в примере в моем посте)

onlinespending 18.03.2022 23:52

Ты как будто потерял меня. Вы переходите от описания того, что происходит, к «Общим ответом на эту проблему является использование». Кажется, должно быть очевидно, что у вас сейчас проблема, но в чем именно является проблема на данный момент? Можете ли вы пояснить, почему выбранное вами разрешение конфликта слияния, которое выбрало строку журнала из «их», но также сохранило новую строку, не то, что вы хотите? Вы даже вставили "как должно быть". Для меня это звучит так, как будто вы получаете то, что хотели, но тогда «решение этого проблема». Опять же, в чем проблема на данный момент?

Lasse V. Karlsen 18.03.2022 23:57

общий ответ на слияние одного файла на самом деле не выполняет слияние, а просто выбирает ту или иную версию. Это не слияние, в котором используется знание общего родительского коммита между двумя расходящимися ветвями. Попробуйте последовать моему примеру и убедитесь сами. НЕТ... Сначала я покажу, что произойдет, если вы просто выполните "git merge -X itss", чего я и хочу. Но я не могу так автоматически разрешать каждый файл. Только этот один файл. И решение этого состоит в том, чтобы использовать «git checkout», который на самом деле не выполняет истинное слияние расходящихся изменений.

onlinespending 19.03.2022 00:02

Это заявление: «И может показаться, что это немного жестко, нет возможности выбирать, хочу ли я «их» или «наших» во время слияния». подразумевает, что вы можете просто разрешать конфликты в режиме реального времени с помощью хорошего визуального редактора конфликтов слияния.

TTT 19.03.2022 04:10

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

jthill 19.03.2022 06:17

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

onlinespending 19.03.2022 17:30
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
60
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Git уже выполняет настоящее трехстороннее слияние для всех объединяемых файлов. Это стандартно, встроено и происходит автоматически.

Однако, если вы спрашиваете, есть ли способ выполнить пользовательскую стратегию слияния для каждого файла из командной строки, то нет. Git предоставляет способ слияния одного файла с помощью git merge-one-file, который выполняет трехстороннее слияние, но не поддерживает пользовательские стратегии слияния: он просто выполняет стандартное трехстороннее слияние.

В целом, однако, цель попытки объединить файл конфигурации попеременно с эквивалентом --ours или --theirs указывает на анти-шаблон в том, как вы храните файлы конфигурации. Обычно это связано с тем, что у людей есть один файл конфигурации с разными версиями в разных ветках (например, для разработки, подготовки и производства). Самый простой способ решить эту проблему — либо сгенерировать файл на основе шаблона с помощью скрипта (например, прочитав правильный выбор из среды или параметра командной строки), либо просто сохранить все три файла в каждой ветке и скопировать или символическая ссылка правильный на место.

В прошлом люди запрашивали функциональность, подобную той, которую вы запрашиваете в качестве опции .gitattributes в списке Git, по тем же причинам, и, как правило, получали совет, приведенный выше.

очевидно, я знаю, что при выполнении «слияния git» происходит трехстороннее слияние. В заголовке написано «один файл», так что спасибо, что обратили мое внимание на «объединение одного файла». Стандартным ответом на «объединение» одного файла является использование «git checkout», который не выполняет трехстороннее слияние, а просто выбирает «наш» против «их», «все или ничего». Мне не нужна настраиваемая стратегия слияния, мне достаточно стандартного трехстороннего слияния в одном файле. Поскольку .gitattributes может применяться к каждому файлу отдельно, я подумал, что поиск решения будет естественной областью. Я попробую «git merge-one-file», и если все пойдет хорошо, я соглашусь!

onlinespending 19.03.2022 16:20

можете ли вы поделиться ссылкой на «git merge-one-file». Я не получаю никаких хитов при поиске этого. Спасибо. Хорошо, я вижу git-scm.com/docs/git-merge-one-file

onlinespending 19.03.2022 16:22

Вам действительно нужно увидеть git merge-one-file -h, потому что документы не говорят вам, как его вызывать, но да, есть справочная страница.

bk2204 19.03.2022 16:27

нужно ли его выдавать после «git read-tree -m» или с «git merge-index»? или я могу просто использовать это после «git merge», чтобы разрешить один файл между разными ветвями?

onlinespending 19.03.2022 16:35
Ответ принят как подходящий

Хотя это немного запутанно, кажется, проще всего сделать следующее.

  1. git merge -Xtheirs --no-commit main Это дает мне трехстороннее слияние, которое я хочу для этих файлов с автоматическим разрешением конфликтующих строк, используя «их»

  2. скопируйте те файлы, которые я хочу объединить в 3-стороннем порядке с автоматическим разрешением «их» в каталог / tmp

  3. git merge --abort чтобы отказаться от слияния

  4. git merge --no-commit main чтобы начать слияние без автоматического разрешения. Естественно, эти конкретные файлы будут иметь конфликты

  5. скопируйте объединенные версии из каталога /tmp обратно в репозиторий

  6. git add <those_files>

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

  8. git commit -m "merged 'main' into 'newbranch'" для завершения слияния. Или git merge --continue можно использовать.

Обломочный git не предлагает более прямого средства для трехстороннего слияния отдельных файлов, но это подойдет.

Определенно круговой, но отрабатывает цель. Вы даже можете повторить шаги 1-3 с ours для набора файлов, которые вы хотите разрешить другим способом. Это тоже было бы легко автоматизировать.

TTT 19.03.2022 17:15

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

onlinespending 19.03.2022 17:31

Верно. Это работает для общего смысла выполнения большого слияния, и, например, сегодня я хочу, чтобы эти 10 файлов автоматически разрешали конфликты с theirs, а эти другие 14 файлов автоматически разрешали конфликты с ours.

TTT 19.03.2022 17:40

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

TTT 19.03.2022 17:41

Другие вопросы по теме