Нет такой вещи, как «приоритет» в слияниях.Ну, там может быть, но вы назначаете его;Вы поймете, что я имею в виду.
Слияние - это объединение работы.Однако для совмещения работы вы и они, кем бы они ни были, должны иметь общую отправную точку.Поскольку Git - это всего лишь коммитов - файлы просто встроены в коммиты - этой общей отправной точкой является shared commit .
Попробуйте, например, использовать git log --graph --decorate --online branch-update branch-jon
.То, что вы увидите, может быть довольно сложным;Я собираюсь нарисовать что-то простое.Git рисует график с более поздними коммитами вверх, а более ранние коммиты ниже;из-за нехватки места я обычно рисую свои графики горизонтально.
Помните, что каждый коммит представляет собой полный снимок всех файлов, замороженных во времени - неизменяемых - и содержит идентификатор хеша своего предшественникаили родительский коммит.Это означает, что начиная с самого последнего коммита, идентифицируемого некоторым именем ветви, мы - или Git - можем работать в обратном направлении, по одному коммиту за раз:
I--J <-- branch-update (HEAD)
/
...--F--G--H
\
K--L <-- branch-jon
В этом (очень упрощенно, до точкиНереально простой пример), у вас с Джоном есть два коммита, каждый из которых вы сделали после общей отправной точки.Фактическая общая отправная точка - фиксация H
(H
- это действительно большой уродливый идентификатор хеша, который git log --oneline
будет печатать сокращенно).Начиная с вашего последнего коммита J
и возвращаясь к I
, а затем H
, и начиная с последнего коммита Джона L
и возвращаясь к K
и затем H
, Git автоматически будет найди этот лучший общий общий коммит.Git называет этот лучший общий коммит базой слияния операции слияния.(Обратите внимание, что коммиты G
и F
и т. Д. Также являются общими; они просто не best .)
(Git может нарисовать этот график как:
* abcdef0 (HEAD -> branch-update) your commit subject
* c9f1233 your previous commit subject
| * 5bf3149 (branch-jon) his commit subject
| * 78983fc his previous commit subject
|/
* 371820d shared commit subject line
[snip]
Несмотря на изменение ориентации, эти два рисунка представляют один и тот же график.)
Ваш фактический график будет намного более запутанным.Может быть неясно, где находится база слияния.Вы можете сделать так, чтобы Git сообщал вам, какой коммит (или, возможно, маловероятный коммит) является базой слияния:
git merge-base --all branch-update branch-jon
Если это приводит к созданию нескольких баз слияний, ситуация немного сложнее, но обычно выв качестве выходных данных мы просто увидим один хеш-идентификатор коммита.
В любом случае, найдя базу слияния - давайте просто назовем ее H
, как на моем рисунке - теперь Git должен сравнить снимок в H
, база слияния, против вашего последнего снимка, J
, чтобы узнать, что вы изменили.Также нужно сравнить снимок в H
с другим последним снимком, чтобы увидеть, что изменил Джон.Таким образом, он запускает две git diff
команды:
git diff --find-renames <em>hash-of-H</em> <em>hash-of-J</em>
: что вы изменили git diff --find-renames <em>hash-of-H</em> <em>hash-of-L</em>
: что изменил Джон
Теперь Git на самом деле объединяет изменения.Он начинается с извлечения всех файлов из коммита H
- из общей начальной точки.К этим файлам применяются ваши изменения.Это также относится к изменениям Джона.В некоторых случаях подать заявление легко: например, если вы изменили файлы F1
и F2
, а Джон не трогал эти файлы, в результате вы получите ваши F1
и ваши F2
.Если Джон дотронулся до F3
, а вы - нет, результатом будет его F3
.Многие другие файлы в H
, вероятно, точно такие же, как в J
и L
, поэтому вы даже не можете определить, использует ли Git H
, J
или L
.,Но для некоторых файлов, таких как F4
, и , вы и Джон внес некоторые изменения.
Именно здесь git merge
действительно приходится много работать.Во всех остальных случаях git merge
получалось просто взять какой-нибудь файл из H
, J
или L
.Для этих файлов - тех, к которым вы обратились - Git действительно должен объединить изменения.
Если вы изменили lИнес с 10 по 15 из F4
, Git принимает ваши изменения в этих строках.Если Джон изменил строки с 20 по 25, Git принимает свои изменения в этих строках.Нет необходимости для приоритета: Git просто принимает оба изменения.
Что если вы оба изменили строки с 30 по 35?Что ж, если вы оба внесли одинаковое изменение в эти строки, Git просто берет одну копию этих изменений, применяя эту одну копию к файлу из H
.
Но если вы оба изменили строки с 30 по 35, и , вы сделали различные изменения, ну, теперь есть проблема. Здесь не существует приоритет, который не существует. По умолчанию Git просто объявляет конфликт слияния и отказывается от завершения слияния.Это оставляет вас в беспорядке: у вас в рабочем дереве есть файл, содержащий маркеры конфликта вокруг конфликтующего набора изменений.Git также оставляет вам все три входных файла в индексе.Как человек, управляющий механизмом Git, ваша задача - навести порядок.
Здесь также появляется «приоритет», который вы можете выбрать . Если вы используетеВ стандартной стратегии слияния recursive
вы можете использовать аргументы -X ours
или -X theirs
, которые я называю расширенными аргументами , хотя Git называет их аргументами стратегии , чтобы сообщить Git: В случае конфликта выберите мои изменения (-X ours
) или его изменения (-X theirs
).
Резюме
Ошибка, которую большинство людей совершают сначалакогда думает о git merge
, думает, что есть только два входа и что работает:
git diff branch-update branch-jon
говорит вам, что произойдет.Это совершенно неправильно!Для каждого слияния имеется три входа: текущий (HEAD
) коммит и его файлы, другой коммит, который вы предоставляете (branch-jon
), и база слияния .Git находит третий вход - который в важном смысле на самом деле является первым входом - сам по себе, из графа commit .График очень важен здесь.График определяет базу слияния, а база слияния, по сравнению с двумя подсказками ветви, определяет результат слияния.
Сам процесс слияния - путь, по которому Git достигает содержимого, которое будет идтив новое слияние - симметрично.Неважно, какой git diff
запускается первым.Однако, когда вы говорите -X ours
или -X theirs
, это нарушает симметрию: вы выбираете одну или другую сторону, чтобы преобладать при возникновении конфликтов.
В конце концов, когда Git делает коммит изобъединенные файлы, что тоже асимметрично.Родитель first нового коммита основан на HEAD
: к какой ветке HEAD
присоединена ветвь, коммит tip этой ветки является первым родителем нового коммита слияния. второй родитель нового коммита является другим коммитом.И, конечно же, новый хэш-идентификатор нового коммита проходит через HEAD
в текущую ветвь, поэтому, когда мы получаем новый коммит слияния M
, его получает ветвь current :
I--J
/ \
...--F--G--H M <-- branch-update (HEAD)
\ /
K--L <-- branch-jon
Обратите внимание, что существование этого коммита слияния с двумя родителями изменяет структуру графа в будущем слиянии .Теперь лучший общий общий коммит между branch-update
и branch-jon
- это коммит L
:
I--J
/ \
...--F--G--H M--N--O <-- branch-update (HEAD)
\ /
K--L--------P <-- branch-jon
Если вы сейчас запустите git merge branch-jon
, Git будет diff L
против O
, чтобы найти то, чтомы изменились, и L
против P
, чтобы найти то, что они изменили.