Git force слить ветку dev с master веткой (перезаписать все локальные файлы) - PullRequest
2 голосов
/ 25 марта 2019

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

Вот шаги, которые я выполнил:

➜  project git:(development) git checkout master
➜  project git:(master) git pull
➜  project git:(master) git merge -s recursive -X theirs development

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.
CONFLICT (modify/delete): vendors/popup/jquery.magnific-popup.min.js deleted in HEAD and modified in development. Version development of vendors/popup/jquery.magnific-popup.min.js left in tree.
CONFLICT (modify/delete): vendors/owl-carousel/assets/animated.css deleted in HEAD and modified in development. Version development of vendors/owl-carousel/assets/animated.css left in tree.

... several similar conflicts

Я попытался сделать rm всех файлов в моей основной ветке

➜  project git:(master) git reset --hard HEAD
➜  project git:(master) rm -rf *
➜  project git:(master) git merge -s recursive -X theirs development

По-прежнему возникают те же конфликты.Что я делаю не так?

Редактировать:

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

enter image description here

Ответы [ 3 ]

2 голосов
/ 25 марта 2019

Я думаю, вы вообще не хотите такого слияния. Я думаю, что вы хотите, это слияние «их стратегии», которое 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.

1 голос
/ 25 марта 2019

При этом значение master будет таким же, как и у вашей ветви development - все, что в master зафиксировано после последнего слияния development, будет потеряно.

git checkout -B master development

0 голосов
/ 25 марта 2019

Ветвь - это просто указатель, а master - просто еще одна ветвь. Если вы не объединяете изменения по ходу работы и dev представляет текущее стабильное состояние проекта, один из вариантов, который избавит вас от прохождения всех конфликтов, будет:

git branch -m master <new-name>
git branch -m dev master

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...