Здесь есть своего рода основная проблема: это не ветви , которые приводят к конфликтам. Или, может быть, лучше: ветви необходимы, но не достаточны. (Но это также зависит от того, как мы определяем branch .)
Чтобы иметь конфликт в Git, вы должны:
- выполнить трехстороннее слияние,используя
git merge
1 - с использованием двух входных коммитов (которые будут приходить из ветвей, но здесь не важны ветви)
- , чьи График коммитов приводит к тому, что Git находит третий базовый коммит коммит, который отличается от двух других входов, и в последнем
- оба эти коммита имеют изменения водни и те же строки одного и того же файла (ов). 2
При всем этом термин fork совершенно не имеет значения. Вилка - это просто клон с некоторой особой семантикой, предоставленной хостинг-провайдером, который предоставил вам кнопку «вилка» с веб-интерфейсом, доступным через веб-интерфейс, или доступный через Интернет API «вилка». Если вы разветвляли хранилище, вы клонировали его. Если вы клонировали свой клон, вы клонировали его снова. Важно то, какие коммиты у вас есть по их хэш-идентификаторам, которые являются их настоящими именами, даже если вы находите этих коммитов по именам ветвей.
Каждый коммитзапоминает хеш-идентификатор своего непосредственного родителя, 3 , в результате чего получается обратная цепочка:
... <-F <-G <-H
, где H
обозначает некоторый хеш-идентификатор фиксации. Commit H
запоминает хэш-идентификатор своего предшествующего коммита. Вместо того, чтобы использовать большой некрасивый хэш-идентификатор, мы просто используем другую букву, чтобы заменить его, поэтому мы называем это G
. Так что commit H
указывает на (содержит хеш-код) commit G
. Коммит G
в свою очередь указывает на F
, который продолжает указывать назад.
Сделав клон или просто набор коммитов в вашем собственном хранилище - все, что имеет значение коммит —У вас теперь есть что-то похожее на это:
I--J <-- branch1
/
...--G--H
\
K--L <-- branch2
ветвь names , branch1
и branch2
здесь, удерживайте идентификатор хеша last коммит в ветке. Git обновил их автоматически, например, когда вы использовали git checkout
в ветке и сделали новые коммиты, так что, когда вы делали новые коммиты, ветвь росла, и имя продолжало указывать на последний коммит. Так что теперь они указывают на коммиты J
и L
соответственно.
Если вы теперь запустите git checkout branch1
и git merge branch2
, Git будет обрабатывать коммиты J
и L
как два входа, которые выобеспечить и самостоятельно - найти лучший общий коммит, который в данном случае, очевидно, является коммитом H
, который находится на обеих ветвях. Затем Git:
- сравнивает все файлы в
H
со всеми файлами в J
- git diff --find-renames H J
- чтобы увидеть, что вы изменили, и - сравнить все файлы в
H
со всеми файлами в L
- git diff --find-renames H L
- чтобы увидеть, что они изменились.
Операция объединения пытаетсяобъединить эти два набора изменений, применяя объединенные изменения к файлам в H
, чтобы создать новый набор файлов, которые можно использовать для моментального снимка в новом коммите слияния.
Если это объединениеуспешно, Git сделает новый коммит слияния M
самостоятельно:
I--J
/ \
...--G--H M <-- branch1 (HEAD)
\ /
K--L <-- branch2
HEAD
здесь указывает, что это ветвь, в которой мы находимся, и, поскольку мы находимся на branch1
, этоимя, которое Git обновляет для хранения хэш-идентификатора нового коммита M
. Commit M
имеет снимок, сделанный путем применения комбинированных изменений к снимку в H
.
Если возникают конфликты слияния, Git останавливается, вообще не делая M
. Git оставляет вам беспорядок для очистки. 4 Вы должны сделать свое собственное объединение, а затем сказать Git, что вы успешно исправили все конфликты и получили правильный результат объединения. Затем вы используете git merge --continue
или git commit
(любой из них), чтобы завершить объединение и сделать коммит M
и снимок, который вы построили.
1 Gон также использует трехстороннее слияние для реализации git cherry-pick
и git revert
, а также несколько других особых случаев, поэтому вы можете получить конфликты слияния без использования git merge
. Но git merge
- это обычный источник, который вы описываете, поэтому вы можете думать об этом как о «когда вы запускаете git merge
» без слишком большой потери точности.
Обратите внимание, что git pull
означает запустить git fetch
, затем запустить вторую команду Git, обычно git merge
. Так что git pull
- это просто выборка + слияние.
2 Есть некоторые неприятные детали о том, что означает "то же самое", как для файлов, которые можно переименовывать, так и для строк, которыеможет просто упираться, а не перекрываться. Но опять же, вы можете думать об этом как о «другом изменении в одной и той же строке одного и того же файла» = «конфликт» и быть достаточно близким.
3 Для коммитов слияния коммит запоминается всех его родителей - обычно только двух - и, следовательно, указывает не только на коммит в ветви, на которой вы были, когда вы сделали слияние, но также и на коммит, который был вершиной ветви, которую вы слилив то время, когда вы произвели слияние.
4 Беспорядочное состояние слияния занимает index , который мы здесь вообще не описывали. Git также оставляет вам свою лучшую попытку объединить конфликтующие изменения в файлах в рабочем дереве и оставляет информацию о слиянии, записанном для git merge --continue
, которая работает git commit
или для git commit
,чтобы сделать коммит слияния.
Вы можете отказаться от всего, используя git merge --abort
, который возвращает все к тому, что было до того, как вы запустили git merge
.