Git слить с силой перезаписать

У меня есть ветвь с именем demo, которую мне нужно объединить с ветвью master. Я могу получить желаемый результат с помощью следующих команд:

git pull origin demo
git checkout master
git pull origin master
git merge demo
git push origin master

Моя единственная проблема заключается в том, что если есть какие-либо проблемы с объединением, я хочу сообщить git о замене изменений в ветке master без запроса со слиянием. Поэтому в основном изменения в ветки demo должны автоматически перезаписываться в ветки master.

Я просмотрел несколько вариантов, но я не хочу рисковать слиянием.

Ответ 1

На самом деле это не связано с этим ответом, но я бы выбрал git pull, который просто запускает git fetch, а затем git merge. Вы выполняете три слияния, которые сделают ваш Git запуск трех операций выборки, когда одна выборка - это все, что вам нужно. Следовательно:

git fetch origin   # update all our origin/* remote-tracking branches

git checkout demo         # if needed -- your example assumes you're on it
git merge origin/demo     # if needed -- see below

git checkout master
git merge origin/master

git merge -X theirs demo   # but see below

git push origin master     # again, see below

Управление самым сложным слиянием

Самая интересная часть здесь git merge -X theirs. Как отметил root545, параметры -X передаются стратегии слияния, а обе стратегии по умолчанию recursive и альтернативная стратегия resolve принимают -X ours или -X theirs (один или другой, но не оба). Однако, чтобы понять, что они делают, вам нужно знать, как Git находит и обрабатывает конфликты слияния.

Конфликт слияния может возникать в некотором файле 1 когда базовая версия отличается от текущей (также называемой локальной, HEAD или --ours) версии, а другая (также называемой remote или --theirs) версии того же файла. То есть, слияние выявило три пересмотра (три фиксации): базовые, наши и их. "Базовая" версия - это база слияния между нашим фиксатором и их фиксацией, как показано в графе фиксации (более подробно об этом см. В других публикациях StackOverflow). Git обнаружил два набора изменений: "что мы сделали" и "что они сделали". Эти изменения (в общем) найдены на линейной, чисто текстовой основе. Git не имеет реального понимания содержимого файла; это просто сравнение каждой строки текста.

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

Если изменения происходят на разных строках, например, мы меняем color на colour в строке 17, и они меняют fred на barney в строке 71 - тогда конфликта нет: Git просто принимает оба изменения. Если изменения происходят в одних и тех же строках, но имеют одинаковые изменения, Git берет одну копию изменения. Только если изменения находятся в одних и тех же строках, но имеют разные изменения или этот особый случай интерферирующего контекста, вы получаете конфликт модификации/изменения.

Параметры -X ours и -X theirs сообщают Git, как разрешить этот конфликт, выбрав только одно из двух изменений: наше или их. Поскольку вы сказали, что вы объединяете demo (их) в master (ours) и хотите изменения от demo, вам нужно -X theirs.

Тем не менее, применение метода -X опасно. Просто потому, что наши изменения не противоречили друг другу, не означает, что наши изменения фактически не конфликтуют! Один классический пример встречается в языках с объявлениями переменных. Базовая версия может объявить неиспользуемую переменную:

int i;

В нашей версии мы удаляем неиспользуемую переменную, чтобы предупредить компилятор, и в их версии они добавят цикл через несколько строк, используя i в качестве счетчика циклов. Если мы объединим два изменения, полученный код больше не компилируется. Опция -X здесь не помогает, так как изменения находятся на разных строках.

Если у вас есть автоматизированный набор тестов, самое главное - запустить тесты после слияния. Вы можете сделать это после совершения и исправить ситуацию позже, если это необходимо; или вы можете сделать это до совершения, добавив --no-commit в команду git merge. Мы оставим детали для всего этого в других сообщениях.


1 Вы также можете получить конфликты в отношении операций с файлом, например, возможно, мы исправим правописание слова в файле (так, чтобы у нас были изменения), и они удалите весь файл (чтобы удалить его). Git не разрешит эти конфликты самостоятельно, независимо от аргументов -X.


Выполнение меньшего количества слияний и/или умных слияний и/или использования rebase

В обеих командных последовательностях есть три слияния. Первый заключается в том, чтобы принести origin/demo в локальный demo (ваш использует git pull, который, если ваш Git очень старый, не сможет обновить origin/demo, но даст тот же конечный результат). Второй - принести origin/master в master.

Мне непонятно, кто обновляет demo и/или master. Если вы пишете свой собственный код в своей ветке demo, а другие пишут код и вставляя его в ветвь demo на origin, то это слияние первого шага может иметь конфликты или создавать реальное слияние. Чаще всего лучше использовать rebase, а не сливать, чтобы объединить работу (по общему признанию, это вопрос вкуса и мнения). Если это так, вы можете использовать git rebase вместо этого. С другой стороны, если вы никогда не выполняете никаких своих обязательств на demo, вам даже не нужна ветвь demo. В качестве альтернативы, если вы хотите автоматизировать многие из них, но, чтобы быть в состоянии тщательно проверить, когда есть коммиты, сделанные вами как вы, так и другие, вы можете использовать git merge --ff-only origin/demo: это ускорит переадресацию вашего demo в соответствии с обновленный origin/demo, если это возможно, и просто просто сбой, если нет (в этот момент вы можете проверить два набора изменений и выбрать реальное слияние или переустановку по мере необходимости).

Эта же логика применяется к master, хотя вы выполняете слияние на master, поэтому вам определенно нужен master. Однако даже вероятнее, что вы хотите, чтобы слияние завершилось неудачей, если оно не может быть выполнено как ускоренное несмешивание, поэтому это, вероятно, также должно быть git merge --ff-only origin/master.

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

git fetch origin   # update origin/*

git checkout master
git merge --ff-only origin/master || die "cannot fast-forward our master"

git merge -X theirs origin/demo || die "complex merge conflict"

git push origin master

Если вы выполняете свой собственный ответ demo, это не помогает; вы можете сохранить существующее слияние (но, возможно, добавить --ff-only в зависимости от того, какое поведение вы хотите), или переключить его на выполнение переадресации. Обратите внимание, что все три метода могут потерпеть неудачу: слияние может завершиться с конфликтом, слияние с --ff-only может оказаться невозможным для ускоренной перемотки вперед, а rebase может завершиться с конфликтом (rebase работает, по сути, за счет сбора вишни, что использует механизм слияния и, следовательно, может получить конфликт слияния).

Ответ 2

Вы можете попробовать "наш" вариант в git merge,

git слияние ветки -X ours

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

Ответ 3

У меня была похожая проблема, когда мне нужно было эффективно заменить любой файл, в котором были изменения/конфликты, на другую ветку.

Решение, которое я нашел, состояло в том, чтобы использовать git merge -s ours branch.

Обратите внимание, что опция -s а не -X. -s обозначает использование ours в качестве стратегии слияния верхнего уровня, -X будет применять опцию ours к стратегии recursive слияния, а это не то, что я (или мы) хотим в этом случае.

Шаги, где oldbranch - это ветка, которую вы хотите перезаписать с newbranch.

  • git checkout newbranch проверяет ветку, которую вы хотите сохранить
  • git merge -s ours oldbranch сливается со старой веткой, но сохраняет все наши файлы.
  • git checkout oldbranch проверяет ветку, которую вы хотите перезаписать
  • get merge newbranch сливает в новую ветку, перезаписывая старую ветку

Ответ 4

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

enter image description here

Прежде чем что-то трогать

git stash
git status # if anything shows up here, move it to your desktop

Теперь приготовьте мастера

git checkout master
git pull # if there is a problem in this step, it is outside the scope of this answer

Оденьте feature

git checkout feature
git merge --strategy=ours master

Перейти на убийство

git checkout master
git merge --no-ff feature