(логически это должен быть комментарий, но мне нужно отформатировать его, чего я не могу сделать в комментарии. Плюс, конечно, он слишком длинный. :-))
В ответе SpoonMeiser (правильный и голосующий) у нас есть этот рисунок:
B1 ,-o--o--o
/
A ,-o--o
/ \
B2 / '-o--o
/ \
master --o--o--o-----------o--
Я чувствую необходимость указать, что этот рисунок немного вводит в заблуждение: он заставляет людей думать, что коммиты в нижнем ряду - это коммиты, которые достижимы с master
, что следующий ряд - это коммиты, достижимые с B2
, что третья строка - это коммиты, доступные с A
, а верхняя строка - это коммиты, доступные с B1
.
Я использовал эту странную фразу, достижимую с , вместо более очевидного слова на . Почему мы называем коммиты достижимые вместо того, чтобы просто говорить, в какой ветке они находятся?
Ответ заключается в том, что коммиты часто находятся на многих ветвях одновременно . Давайте дадим каждому из этих коммитов однобуквенное имя (начиная с C
, чтобы избежать A
и B
), и переместим имена веток в мою любимую позицию, в вправо , вот так :
,-E--F--G <-- B1
/
,-D--H <-------- A
/ \
/ '-I--J <-- B2
/ \
...--C--K--L-----------M <-- master
Способ, которым Git обрабатывает эти коммиты, заключается в том, чтобы начать с коммита, на который указывает каждое отдельное имя: для master
, это коммит M
, тогда как для * ветвления это коммит H
. например, J
для ветви B2 - Git затем работает в обратном направлении : J
приводит к I
, затем к H
и D
и C
и так далее. Таким образом, ветка B2 содержит всех этих коммитов . Между тем ветвь master
начинается с M
, что ведет к J
и L
. Таким образом, master
содержит каждый коммит, который B2
в настоящее время содержит, а также коммиты L
и K
.
Стратегия, которую следует использовать для git merge
, зависит от того, какой результат вы хотите получить в будущем. То есть мы должны сейчас планировать то, что будем делать завтра, или на следующей неделе, или в следующем году.
Что делает git checkout
, это извлекает коммит, на который указывает имя ветви. Например, если мы git checkout
ответвим B1, мы получим commit G
. Снимок, сохраненный в G
, попадает в наш index (из которого мы сделаем следующий коммит, когда мы доберемся до этой точки) и в наше work-tree (где мы действительно может видеть и работать с файлами). Если мы сделаем новый коммит, то получим новый хеш-идентификатор - мы будем использовать следующую букву после M
здесь или N
и нарисуем ее:
,-E--F--G--N <-- B1 (HEAD)
/
,-D--H <-------- A
/ \
/ '-I--J <-- B2
/ \
...--C--K--L-----------M <-- master
Обратите внимание, что git commit
обновил текущую ветку - что Git знает, потому что git checkout
прикрепил HEAD
к имени ветви - так что теперь он указывает на только что сделанный новый коммит. Новый коммит указывает на коммит, который мы git checkout
-оедали, то есть тот, который был текущим, пока мы не сделали новый.
Что делает git merge
, так это находит * 10 * * базу слияния между текущим коммитом (теперь N
) и коммитом, который мы называем. Если мы говорим git merge A
, имя A
переводится в commit H
, поэтому Git находит commit H
в графе и находит commit N
в графе, а затем работает в обратном направлении через этот граф, чтобы найти best common commit - лучший коммит на обеих ветвях. «Лучший», в широком переводе, означает «ближе всего к двум концам», так что здесь это коммит D
.
Действие слияния - часть глагола для слияния - объединяет коммиты N
и H
относительно D
, фактически выполняя две команды git diff
:
git diff --find-renames <hash-of-D> <hash-of-N> # what we changed
git diff --find-renames <hash-of-D> <hash-of-H> # what they changed
Git выясняет, какие файлы мы изменили, и что мы с ними сделали. Затем он выясняет, какие файлы они изменили, и что они сделали. Тогда он просто (ха) объединяет два набора изменений. Эти объединенные изменения применяются к снимку в базе, здесь D
, чтобы создать новый снимок.
имея комбайнЕсли изменения успешно - по крайней мере, пока Git считает, что им удалось их объединить - Git продолжит делать коммит слияния , который, как и M
, имеет двух родителей,Первый родитель будет коммитом N
, а второй родитель будет коммитом H
, который мы выбрали для слияния.Результат добавляется в ветку HEAD
как обычно, поэтому мы получаем:
,-E--F--G--N--O <-- B1 (HEAD)
/ ,----------'
,-D--H <-------- A
/ \
/ '-I--J <-- B2
/ \
...--C--K--L-----------M <-- master
Если бы мы сейчас попросили Git объединить B1 и B2, Git пошёл бы по этому графику, чтобы найти лучший "общийотправная точка".Без коммита O
общей начальной точкой является коммит D
, но с O
общей начальной точкой является H
.
Во всех случаях эти "нормальные" команды Git просто добавить график.Каждый новый коммит имеет одного родителя, если это обычный коммит, или два, если это коммит слияния.Дополнения изменяют форму графика, с изменением слияний, которое фиксирует слияние future , которое будет выбрано в качестве его базы слияния.Поэтому слияния, которые мы делаем сейчас, - это те, которые нам нужны, чтобы сделать что-то сейчас, но они также являются теми, которые мы делаем сейчас, чтобы сделать слияния, которые мы могли бы сделать позже, проще.
Есть некоторые команды Git, которые могут remove фиксации, заставляя имя ветви указывать на другое (старое) место в графе, вместо добавления новых фиксаций в графе.Команда git reset
особенно хороша в этом, но git rebase
также делает это: сначала она копирует кучу коммитов в «новые и улучшенные», затем перемещает имя ветви так, чтобы указывать на последнюю из копий.сделал.Как правило, удаленные коммиты не исчезают немедленно: Git пытается дать вам месяц или больше, чтобы передумать и вернуть их обратно.Но их становится все труднее найти, потому что способ, которым Git обычно находит коммиты, состоит в том, чтобы начать с имени ветви (или имени тега) и посмотреть один коммит, который находит это имя, а затем использовать этот коммитработать в обратном направлении через график.