Я думаю, вы вообще не хотите такого слияния. Я думаю, что вы хотите, это слияние «их стратегии», которое Git не предлагает. Это еще можно сделать: см. ответ VonC здесь . Но читайте дальше, чтобы убедиться, что - это то, что вы хотите, прежде чем использовать его.
Здесь происходит две важные вещи, каждая из которых захвачена вашей последовательностью команд:
git checkout master
git merge -s recursive -X theirs development
(я пропустил шаг, который ничего не делал).
Во-первых, большинство слияний - в том числе из стратегии слияния -s recursive
по умолчанию - работают, находя общую отправную точку: общую базу слияния коммит. Чтобы нарисовать это как довольно простой случай, рассмотрим следующий граф коммитов:
C--D--E <-- master (HEAD)
/
...--o--B
\
F--G--H <-- development
Очевидно, что ваш собственный график будет намного более привлекательным, но в конце концов, все работает одинаково: Git получает текущий коммит , как указано вашей текущей веткой master
, которая является коммитом E
, как один из трех входов в процесс слияния. Git берет коммит, который вы указываете , в данном случае коммит H
, потому что development
- это тот, на который вы сказали посмотреть, как второй из трех входов в процесс слияния.
Вход третий рассчитывается по графику. Мы начинаем с каждого из двух коммитов и начинаем идти назад, как всегда делает Git. Фиксация E
приводит к D
, что приводит к C
, а затем к B
. Между тем фиксация H
приводит к G
до F
до B
. Коммит B
находится на обеих ветвях, и это, в терминах графа, наименьший общий предок . 1 (Неформально это один из ближайших к обоим Советы по переходу. «Ближайший» разбивается на сложные графы, но для простых, таких как этот, это то, что означает наименьший общий предок.) Это делает его лучшим совместно используемым коммитом и, следовательно, базой слияния.
То, что происходит сейчас, довольно просто в общих чертах, но застревает в некоторых деталях. Git начинается с коммита base merge в терминах «желаемого результата». То есть содержимое, с которого начинается Git, - это содержимое снимка в коммите B
. К этому содержимому Git необходимо применить комбинацию всех изменений, которые вы внесли, и любых изменений, которые они внесли. Так что Git нужно запустить две git diff
команды:
git diff --find-renames <em>hash-of-B</em> <em>hash-of-E</em>
: что мы изменили на master
git diff --find-renames <em>hash-of-B</em> <em>hash-of-H</em>
: что они изменили на development
Если база слияния близка к обоим подсказкам, как здесь, то, вероятно, каждый из этих двух различий показывает не так уж много. Объединение двух наборов изменений будет простым и понятным. Если мы изменили строку 12 в README.md
, а они вообще не коснулись README.md
, Git принимает наше изменение, предоставляя нашу версию README.md
для перехода в новый коммит слияния. Если они коснулись другой строки README.md
, Git объединяет наши два изменения: оба применяются к B:README.md
, чтобы создать README.md
для нового коммита. Этот процесс повторяется для всех файлов.
Если мы и они оба коснемся одинаковых строк (ы) одних и тех же файлов, Git обычно объявляет конфликт слияния и останавливается, оставляя нас для очистки беспорядка. Вот тут и появляется опция -X theirs
: это опция eXtended , переданная в стратегию -s
. 2 В этом случае стратегия recursive
обрабатывает theirs
extended-option в значении: в случае конфликта, отбросить мои изменения и использовать их .
Обратите внимание, что если не конфликт, Git будет использовать наше изменение! Иногда это то, что мы хотим. Если это является , что мы хотим, -X theirs
является правильной идеей. Если это , а не , что мы хотим, -X theirs
- неправильная идея. Так вот, где вам нужно решить: используем ли мы -X theirs
или -s theirs
, который мы должны построить, как в ответе VonC?
1 Это самый низкий общий предок, потому что компьютерщики рисуют свои деревья вверх ногами:
A
|
B
/ \
C D
/ \ \
E F G
Общие предки E и F здесь - это C, B и A, но C - это низший один.Общие предки F и G - B и A;B самый низкий.Граф коммитов Git - это ориентированный ациклический граф или DAG, а не дерево, поэтому «LCA» не так прост, как в дереве, и может быть несколько LCA (или вообще без LCA), учитывая два узла в графе,Git обрабатывает все это разумно, для некоторого определения разумного.
2 Git вызывает -X
вариант стратегии , но это звучит в точности как аргумент -s <em>strategy</em>
,Это буквально вариант стратегии, отсюда и неудачное название Git.Я думаю, что расширенный параметр является лучшим именем, отчасти потому, что он объясняет -X
.
Если вы не хотите -X theirs
: см. Другой ответ, который я связал
Больше нечего сказать: -s theirs
когда-то была стратегией слияния Git.Это больше неВы все еще можете синтезировать это любым способом.На самом деле мой фаворит - разновидность команды слесарного дела в ответе Михала Солтиса .
Если вы действительно хотите -X theirs
: почему он жалуется на конфликты?
Я упоминал вышечто процесс слияния - для слияния часть git merge
- "довольно прост ... но затягивает в некоторых деталях".Вы только что нажали одну из этих деталей.
Массивные git diff
s, которые Git получает от различий в найденной им базе слияния, против каждого из двух указанных вами коммитов, содержит множество файловкоторые были полностью удалены с одной стороны, но изменены с другой стороны:
...--B--o--o--...--o--o <-- master (HEAD)
\
o--o--...--o--o <-- development
Где-то в этой огромной цепочке анонимных o
фиксируется вдоль вершины, "мы" (база против хозяина) удалено некоторый файл.«Они» (база против разработки) изменили этот файл.Git не знает, как объединить «удаленное» с «измененным».
Я называю эти высокий уровень конфликтами, потому что они скорее являются изменениями самой природы или существования файла.чем отдельные строки в файле.Ну, я также называю это так, потому что Git сам называет ту часть кода, которая объединяет отдельные строки, «драйвер слияния низкого уровня», поэтому они должны быть противоположного уровня high .Такие конфликты - будь то добавление / добавление, изменение / удаление, переименование / удаление или что-то еще - всегда приводят к остановке -s recursive
git merge
и позволяют исправить ситуацию.Расширенные опции предоставляются только для слияния low level driver , вызванного стратегией.
Фактически, единственная стратегия, которая не Стоп - это стратегия -s ours
.Когда вы используете стратегию -s ours
, эта полностью игнорирует то, что находится в «их» коммите.Git даже не смотрит на базу слияния - он просто использует то, что есть в нашем коммите в качестве результата слияния.
Конечно, -s ours
принимает наше .Вы хотели что-то, что заняло их , по крайней мере, для этого конкретного случая, может быть, для всех случаев.Но если вы действительно не хотите получить эквивалент -s theirs
, а скорее -X theirs
с получением всего их файла в этих случаях, вы застряли с разрешением каждого из этих конфликтов по факту.
Вы можете сделать это с помощью сценария (который вы должны написать самостоятельно).Это немного сложно, хотя.Хитрость заключается в использовании git ls-files --stage
: вы увидите, что для каждого файла, на который жаловался Git:
CONFLICT (modify/delete): vendors/popup/magnific-popup.css deleted in HEAD and modified in development. Version development of vendors/popup/magnific-popup.css left in tree.
Git будет иметь запись на первом этапе для имени (vendors/popup/magnific-popup.css
),и еще одна запись на этапе № 3 (у них), но нет записи на этапе № 2 (у нас).Чтобы решить эту проблему, используйте git add
, чтобы записать запись этапа 3 из рабочего дерева в индекс на нулевом этапе: git add
удалит копии первого и третьего этапов.Вы можете просто собрать все такие имена файлов и передать их всем git add
.