2016-03-12 5 views
2

Я новичок от Git и пытается узнать, как работает git merge. Поэтому я попробовал простой проект git, например:Git merge влияет на рабочий каталог и промежуточную область

У меня есть две ветки: master и b. Вы можете увидеть журнал GIT для обоих здесь (1,2,4 приведены примеры файлов, которые создаются в отраслях):

* cf3456b (HEAD, b) add and modify 4 in b 
* 68b9086 edit 1 in branch b 
| * 81e6490 (master) remove 1 from branch b1 
| * e0a6844 modify 2 in branch b1 
| * 06bad1d add2 in branch b1 
|/ 
* c667d3b add 1 in branch master 

Так
master имеет только один файл: 2
b имеет только два файла: 1, 4

Теперь, когда я пытаюсь сделать:

git merge master 

Я видел это меня ssage:

CONFLICT (изменить/удалить): 1 удален в мастер и изменен в HEAD. Версия HEAD из 1 слева в дереве. Ошибка автоматического слияния; исправить конфликты , а затем зафиксировать результат.

Это странно для меня, я думал раньше, что:

  1. merge Подобно checkout пытается скопировать содержимое из последнего коммита из merge in филиала в последний коммит из merge into ветви, но противоположен checkout, merge пытается чтобы не изменять файл в merge into. И, наконец, если оба merge in и merge into ветви имеют одинаковый файл с другим статусом, возникает конфликт.
  2. Я знаю, что git сохраняет только snapshot файлов вместо differences. Итак, как узнать об изменениях в последних сделках филиалов? (В сообщении вы можете увидеть 1 deleted in master and modified in HEAD, которые являются различия)

Теперь, если моя мысль была правда, никакого конфликта не должно происходить для файла 1. Так что моя мысль не соответствует действительности, но как работает merge?

+0

Потенциальный Дубликат (хотя они и не вдаваться в подробности): http://stackoverflow.com/q/14961255/ 1256452 – torek

ответ

4

Re point # 2, вы правы, что git сохраняет моментальные снимки. Однако, учитывая любые два моментальных снимка a и b, мы можем получить набор изменений (разницу) путем сравнения b против a -and git делает именно это, при необходимости.

Что касается самого слияния, во-первых, следует отметить, что мерзавец держит фиксации граф (также называют «ДАГ» или «фиксации DAG», как граф является D irected циклический G raph или DAG). Это то, что показывает ваш вывод git log --graph, хотя в этом конкретном случае у нас есть такая простая форма графика, что это просто дерево (пока не произойдет слияние).

С любым деревом, и со многими группами DAG, данными два узла в дереве можно найти уникальный L owest C ommon ncestor узел или LCA. Два узла здесь - концы двух ветвей - коммиты cf3456b (конец вашей текущей ветви) и 81e6490 (кончик master) - и LCA в данном конкретном случае является первым фиксатором, c667d3b. В простых случаях, таких как LCA, легко заметить визуально: вы просто просматриваете граф фиксации, чтобы найти место, в которое соединяются две ветви (все оттуда, обратно к корню, находятся на и ветвей).

Этот узел LCA является базовым основанием . Git сначала находит базу слияния текущего фиксации и аргумент, который вы ему даете. (Для «слияния осьминогов», где вы направляете git, чтобы объединить несколько коммитов в текущую ветвь, работа немного больше задействована, но мы можем просто игнорировать их здесь.)

Далее, учитывая существующее основание слияния и два фиксированных кончика, git должен вычислить два набора изменений: один из базы слияния и текущий фиксатор, а один из базы слияния в аргумент commit. Обратите внимание, что git делает это один раз, спереди, для всего процесса, после чего он может продолжить действие слияния.

Теперь, для каждого файла, для которого есть изменения, git должен объединить изменения. Для большинства простых модификаций метод прост: если файл 1 изменен только в одной ветви, выполните модификацию как есть. Если он изменен в обоих ветвях, попытайтесь объединить изменения, взяв только одну копию, где обе ветви сделали одно и то же изменение. Конечно, вы получаете конфликт, если обе ветви делали разные изменения в одну и ту же область одного файла.

Для случаев создания или удаления файлов или при переименовании все становится немного сложнее. Если файл удаляется в одном ветви и нетронутым в другом, git может разрешить это, удалив файл («сохраняя его», если он уже удален в HEAD, удалив его, если он удален в другой фиксации). Если файл переименован в одну ветку и изменен в другом, git также может объединить эти изменения (выполнение или сохранение переименования при одновременном импорте или сохранении других изменений). Однако для чего-то еще, git просто объявляет конфликт, бросает свои метафорические руки и заставляет вас разрешить конфликт.

В этом случае файл 1 действительно был изменен в вашей текущей ветке и действительно был удален в master. Git не уверен, следует ли удалять файл (по указанию слияния-базы до master diff) или сохранять изменения (как указано в базе слиянием до HEAD diff), поэтому он оставляет вас с файлом и конфликтом.

Если вы создали новый файл 5 в обеих ветвях, git снова даст вам конфликт (за исключением, возможно, если новое содержимое файла одинаково в обеих ветвях - я не тестировал этот случай).


Со сложным DAGs может быть несколько минимальными общими кандидатами предок. Для «рекурсивного» слияния git git обрабатывает это путем слияния каждого кандидата LCA для создания новой «виртуальной базы». Эта виртуальная база становится базой слияния, против которой сравниваются две коммиты.Если есть только два кандидата LCA, виртуальная база слияния получается делать грубый эквивалент:

git checkout -b temp candidate_1 
git merge candidate_2 
git commit -a -m "" 

Любые слияния конфликтов, которые происходят во время этого «внутреннего слияния» игнорируется: конфликтующее слияние базы используются с его конфликты на месте. Это может привести к некоторым конфликтам, похожим на фанки, особенно когда в одном и том же регионе «внешнего» слияния есть конфликт: см. this SO question.

Если есть более двух кандидатов LCA, merge-recursive берет первые два и объединяет их, затем объединяет третье, затем объединяет четвертое и так далее, итеративно. Он делает это, даже если он может объединять пары, чтобы сократить число пополам, затем объединить объединенные пары, чтобы разрезать его пополам и так далее. То есть, учитывая кандидатов N LCA, можно было бы слить ceil (log2 (N)), а не слияние N-1, но git doesn't-N редко превышает 2 в любом случае.

+0

У меня есть еще один вопрос о 'git merge', который, кажется, нарушает ваш прогноз. См. Его: http://stackoverflow.com/questions/35969245/why-merge-does-not-cause-conflict – hasanghaforian

0

сообщение конфликт:

КОНФЛИКТ (удалить/изменить): 1 удален в мастера и модифицирована в ГОЛОВЕ

означает, что 1 был удален в главной ветви, которую вы сливаете, но изменен в HEAD (в филиале вы на самом деле сейчас).

Таким образом, вы должны решить, будет ли

remove file using "git rm 1" 

или

accept version from HEAD with "git add 1" 
Смежные вопросы