TL; DR
Более того, мы обычно склонны думать о ревизиях как происходящих в хронологической последовательности и ожидаем, что Git будет работать так же.
Этоужасная ошибкаGit работает в порядке графиков , а не в хронологическом порядке.Когда порядок графика хронологический - что иногда, но не всегда - , тогда работает нормально.
Long
(я даже не буду касаться ребазздесь. Для правильного обсуждения перебазирования см. более ранние публикации. Я просто собираюсь сосредоточиться на симметрии в слиянии и местах, где могут показать швы.)
Более кратко, сделайтеНаправление слияния и / или порядок слияния имеют разные результаты в результате слияния?Я подозреваю, что это не имеет значения.
Ответ и нет, и да.
Чтобы понять почему это ответ, вам нужно понять несколькоо Git.
Во-первых, давайте рассмотрим, как Git обрабатывает коммиты, в отличие от того, сколько других делают это (большинство других? все остальные? должна быть хотя бы одна другая VCS, которая делает это тоже ...),В системах контроля версий процесс создания коммита в ветви добавляет новый коммит к ветви;но в Git набор ветвей, которые содержат коммит после его выполнения , может быть и может динамически изменяться.Таким образом, сделанный после этого коммит не зависит от какой-либо ветви и может, если хотите, быть выполнен на no ответвлении.
В Mercurial дляНапример, это совершенно невозможно.В то время как Mercurial во многом очень похож на Git, существование коммита требует существования его ветви.Фиксация сделана на этой ветви, и навсегда часть этой ветви.Это ветвь only , которая содержит фиксацию: коммит постоянно прикрепляется к своей ветке.
В Git и Mercurial каждый коммит имеет уникальный идентификатор, и каждый коммит хранит уникальныйИдентификатор его родительского (единственного) коммита, если это обычный коммит.Следуя этой обратной цепочке, начиная с last commit и работая в обратном направлении, мы можем найти историю коммитов на ветке:
... <-grandparent <-parent <-child
В Mercurial эти коммиты навсегдав их ветке, поэтому мы можем написать название ветви слева:
branch-A: ...--I--J
Найти последний коммит в Mercurial легко, поскольку коммиты имеют простой порядковый номер в локальном репозитории (вместе с уникальным идентификатором хеша)используется для совместного использования между репозиториями).
В Git ветка names является подвижной.Перед тем как коммит J
существует, имя branch-A
хранит необработанный хеш-идентификатор коммита I
:
...--H--I <-- branch-A
При создании нового коммита Git просто записывает хэш-идентификатор нового коммита в имя ветви, так что ветвь указывает на новый коммит:
...--H--I--J <-- branch-A
Слияние - это и существительное, и глагол
В управлении версиями объединение , глагол или объединить некоторые коммиты, это процесс.Результат - по крайней мере, как в Git, так и в Mercurial - это коммит слияния , где мы используем слово слияние в качестве прилагательного, модифицирующего коммит , или для краткости, слияние (существительное).
Что делает коммиты слияния be коммиты слияния в том, что они имеют двух родителей, приносящих две линии развитиявместе:
...--I--J
\
M
/
...--K--L
ОнОпять же, Git и Mercurial в основном одинаковы, с одним действительно важным отличием: в Mercurial коммит слияния находится конкретно на одной ветви, которая является той ветвью, на которой вы находитесь, когда запускаете hg merge
.Коммиты, включая коммиты слияния, постоянно прикрепляются к их ветвям.Как только процесс слияния завершен и коммит слияния существует, этот коммит слияния находится в этой одной ветви.В Git коммит слияния идет на текущую ветку, но поскольку мы можем переместить name , в некотором смысле это не имеет значения.
Но мы должны рассмотреть обе части:часть глагола, , чтобы объединить , а затем близко к части существительного, объединение .(Помимо: Git также позволяет коммитам слияния иметь более двух родителей, в то время как Mercurial ограничивает каждое слияние двумя родителями. Нет ничего такого, что эти фанки слияния, которые Git называет слияниями осьминога , могутсделать то, что вы не можете сделать с помощью серии попарных слияний, но в некоторых случаях слияние осьминога может показывать намерение более четко.)
Чтобы объединить, глагол
Предположим, например, что branch-A
и branch-B
были такими до слияния:
...--I--J <-- branch-A
...--K--L <-- branch-B
Прежде чем мы сможем объединить их, мы должны отследить обе истории назад достаточно далеко, чтобы найти слияние base , общий коммит, от которого эти две линии развития расходились.(Это верно и в Mercurial.) Итак, давайте добавим немного больше:
I--J <-- branch-A
/
...--H
\
K--L <-- branch-B
Здесь, коммит H
является общей отправной точкой.В Git commit H
находится на в обеих ветвях одновременно (но не в Mercurial).Тем не менее, обе системы управления версиями выполняют процесс для объединения практически одинаково: вы начинаете с выбора одного коммита для проверки, такого как коммит J
с использованием git checkout branch-A
или hg update branch-A
.Затем вы выбираете другой коммит, используя команду merge
глагол: git merge branch-B
или hg merge branch-B
.VCS находит соответствующую базу слияния, то есть commit H
, и сравнивает содержимое H
с содержимым J
, чтобы увидеть, что you изменилось:
git diff --find-renames <hash-of-H> <hash-of-J> # what we changed
и повторяет то же сравнение, чтобы найти то, что они изменили:
git diff --find-renames <hash-of-H> <hash-of-L> # what they changed
Git объединяет эти изменения в одно большое "все изменения", применяет эти изменения к base (commit H
) и делает новый коммит.
(Mercurial делает то же самое, хотя есть некоторые очень важные различия с точки зрения того, как Mercurial знает о переименованиях. Это имеет значение, только если вы переименовалинекоторые файлы, начиная с базы слияния. Если у вас есть переименованные файлы, и у вас есть конфликт переименования / переименования, порядок слияния становится очень важным. Но давайте предположим, что проблем с переименованием нет.)
Самое интересное здесь то, что вы можете контролировать как объединяются изменения.В Git вы делаете это с помощью стратегии слияния и расширенных аргументов стратегии ;в Mercurial вы делаете это с помощью выбранного инструмента слияния .Если вы используете «нашу» стратегию / инструмент, VCS полностью игнорирует их изменения: объединенные изменения - это просто ваши изменения, поэтому VCS выполняет новый коммит слияния, используя тот же код как в текущем коммите.Здесь очевидно, что порядок слияния имеет значение: если мы игнорируем их изменения, нам лучше всего быть уверенными, кто "мы"!
Даже без "нашей" стратегии могут возникнуть конфликтымежду вашими изменениями и их изменениями.Если так, то и Git, и Mercurial могут сказать: предпочитают мой или предпочитают их .Это даст разные результаты, поэтому и здесь порядок слияния имеет значение.Конечно, на данный момент есть симметричный вариант: выберите мой или выберите их .Если вы поменялись ролями, вы можете поменять параметры, поэтому, хотя порядок важен, он менее важен.
Слияние, существительное
Давайте предположим, что нет никаких конфликтов и никаких особых наших / их событий, и запустим git checkout branch-A; git merge branch-B
.Если все идет хорошо, VCS делает коммит слияния M
со своими двумя родителями:
I--J
/ \
...--H M <-- branch-A
\ /
K--L <-- branch-B
В Mercurial порядок слияния здесь имеет значение, потому что как только коммит сделан M
, он привязывается к еговетка навсегда.Но Git позволяет нам перемещать имена ветвей по факту.Мы можем сделать M
таким, оставить его там, где он есть, толкнуть branch-A
на один шаг назад, чтобы снова указать J
, а затем переместиться на branch-B
вперед, чтобы указать M
, давая:
I--J <-- branch-A
/ \
...--H M <-- branch-B
\ /
K--L
, что в значительной степени совпадает с тем, что мы получили бы, если бы сделали 1204 *.Таким образом, похоже, что здесь порядок слияния не имеет значения (при условии, что во время части глагол не было зацепок).Но здесь на диаграмме чего-то не хватает, и это понятие first и not-first родителей!
first родитель слиянияcommit M
, как в Git, так и в Mercurial, является родителем "той же ветки".То есть, поскольку мы выполняли коммит J
, когда мы запустили слияние, первый родительский элемент M
- это коммит J
.Это оставляет L
в качестве второго родителя.Если мы переместим метки веток в Git, мы можем сказать, что сделали это, потому что первый и второй родители все еще будут в другом порядке.
Итак, мы видим здесь, что для Git, даже когда для слияния У глагола нет проблем с упорядочением, результирующий коммит слияния (прилагательное или существительное) действительно имеет порядок.Вы, пользователь, должны решить, имеет ли значение этот заказ для вас.Если нет, симметрия доступна.Если порядок родительского коммита имеет значение, направление / порядок слияния в Git так же критичен, как и в Mercurial.
График является ключом
В любой системе контроля версий на основе графа- следовательно, и в Git, и в Mercurial - график является историей.В частности, в Git нет такой вещи, как история файлов; график - это не только история primary , это история only .(Mercurial хранит данные файла более обычным способом, поэтому существует история файлов истории и , но история файлов в любом случае бесполезна без истории изменений.)
Чтобы иметь возможностьчтобы понять, что делает Git и почему, вам нужно знать достаточно теории графов, чтобы ездить или ездить на общественном транспорте по городу.Это не так уж много, но если вы не понимаете, что у Git есть эти односторонние железнодорожные пути / улицы / мосты, ведущие от last к обратному направлению к first ,многие вещи не будут иметь смысла.Git начинается в конце и работает обратно к началу.Имена ветвей приводят вас к графику в конце подсказки.Все остальное является функцией графа фиксации.