Ответ 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 работает, по сути, за счет сбора вишни, что использует механизм слияния и, следовательно, может получить конфликт слияния).