Git слияние с принудительной перезаписью

109

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

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

Меня беспокоит только то, что если есть какие-либо проблемы слияния , я хочу сказать gitперезаписать изменения в masterветке, не давая мне приглашения на слияние. Таким образом, в основном изменения в demoветке должны автоматически перезаписывать изменения в masterветке.

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

OpenStack
источник
git push -f origin master
MD XF
4
@MDXF: Может быть, я ошибаюсь, но мне не следует использовать -fпараметр с mergeкомандой, а не с pushкомандой
OpenStack
Вы можете попробовать оба и посмотреть, что вам
подходит
См. Ссылку ниже для решения проблемы принудительной перезаписи: stackoverflow.com/a/42454737/984471
Манохар Редди Поредди

Ответы:

109

На самом деле это не связано с этим ответом, но я бы отказался от него 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) версии, так и от другой (также называемой удаленной или --theirs) версии того же файла. То есть слияние выявило три ревизии (три коммита): базовую, нашу и их. «Базовая» версия взята из базы слияния между нашей фиксацией и их фиксацией, как показано на графике фиксации (для получения дополнительной информации см. Другие сообщения StackOverflow). Затем Git обнаружил два набора изменений: «что мы сделали» и «что они сделали». Эти изменения (как правило) находятся построчно, чисто текстовымиоснование. Git не имеет реального понимания содержимого файлов; это просто сравнение каждой строки текста.

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

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

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

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

int i;

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

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


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


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

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

Мне не понятно, кто обновляет demoи / или master. Если вы пишете свой собственный код в своей demoветке, а другие пишут код и отправляют его в demoветку origin, тогда это слияние на первом этапе может иметь конфликты или производить настоящее слияние. Чаще всего для объединения работы лучше использовать rebase, чем merge (правда, это вопрос вкуса и мнения). Если это так, вы можете использовать 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может не иметь возможности быстрой перемотки вперед, а перебазирование может завершиться сбоем с конфликтом (перебазирование, по сути, работает с помощью коммитов выбора вишни , которые используют слияние машины и, следовательно, может возникнуть конфликт слияния).

торек
источник
29

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

Я нашел решение - использовать 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 сливается в новую ветку, перезаписывая старую ветку
Найл Маккормак
источник
Примечание: ни одно из других решений здесь не помогло мне, поэтому я отправляю свой ответ.
Найл Маккормак 09
1
У меня это не сработало. Он разрешил конфликт (разрешил конфликтующие файлы), но файл не был объединен. И не может ни слиться.
c-an
Если кто-то застрял там, где вам предлагается: «Пожалуйста, введите сообщение фиксации, чтобы объяснить, почему это слияние необходимо»: введите свое сообщение, затем нажмите клавишу ESC на клавиатуре, введите: wq и нажмите ENTER, чтобы выйти из приглашения.
topherPedersen
спасибо @NiallMccormack за публикацию этого ответа, он нам очень помог!
Raphael_S
20

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

введите описание изображения здесь

Прежде чем прикоснуться к чему-либо

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
Уильям Энтрикен
источник
1
git push to finish
Коччи
git-scm.com/docs/git-merge#Documentation/git-merge.txt-ours. Это работало, когда коммиты не сливались чисто. Но, если цитировать источник, такой подход будет работать не всегда Changes from the other tree that do not conflict with our side are reflected in the merge result. У меня это не сработало, так как в моей ветке были чисто слитные коммиты, которые перезаписывались. Есть ли другой способ?
NiharGht,
11

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

git merge branch -X наш

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

корень
источник
3
Это было бы наоборот, поскольку ОП сказал, что он хочет, чтобы demoверсия была предпочтительнее, даже если он сливается с master. Также есть -X theirs, но неясно, чего действительно хочет ОП.
torek
Вы не прочитали весь путь. В вашей заметке описывается, что oursпроисходит при использовании на бэкэнде с -sопцией. При использовании ourswith -X: отбрасывает все, что делало другое дерево, объявляя, что наша история содержит все, что в ней произошло. источник
jimasun
2

Когда я пытался использовать -X theirsи другие связанные переключатели команд, я продолжал получать коммит слияния. Я, наверное, неправильно это понял. Одна из простых для понимания альтернатив - просто удалить ветку и снова отследить ее.

git branch -D <branch-name>
git branch --track <branch-name> origin/<branch-name>

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

Мэтт
источник