Mercurial: именованные ветви против нескольких репозиториев

в настоящее время мы используем subversion на относительно большой кодовой базе. Каждый выпуск получает свою собственную ветвь, а исправления выполняются против ствола и переносятся в ветви выпуска с помощью svnmerge.py

Я считаю, что пришло время перейти к лучшему управлению версиями, и я некоторое время играл с Mercurial.

Кажется, есть две школы, хотя по управлению такой структурой выпуска с использованием Mercurial. Либо каждый релиз получает свое собственное РЕПО, а исправления сделано против ветви выпуска и нажата на основную ветвь (и любые другие более новые ветви выпуска.) Или использование именованных ветвей в одном репозитории (или нескольких совпадающих копий.)

в любом случае кажется, что я могу использовать что-то вроде трансплантации изменений cherrypick для включения в ветви выпуска.

6 ответов


самая большая разница заключается в том, как имена ветвей записываются в истории. С именованными ветвями имя ветви встроенный в каждом наборе, и таким образом станет неизменной частью истории. С клонами будет постоянного запись того, откуда взялся конкретный набор изменений.

это означает, что клоны отлично подходят для быстрых экспериментов, где вы не хотите записывать имя ветви, а именованные ветви хороши для долгосрочной перспективы филиалы ("1.x", " 2.x " и тому подобное).

обратите внимание также, что один репозиторий может легко разместить несколько легких ветвей в Mercurial. Такие ветви в репозитории могут быть закладки, так что вы можете легко найти их снова. Предположим, что вы клонировали репозиторий компании, когда он выглядел так:

[a] --- [b]

вы рубить и сделать [x] и [y]:

[a] --- [b] --- [x] --- [y]

имею в виду, пока кто-то ставит [c] и [d] в репозиторий, поэтому, когда вы тянете, вы получаете график истории, как это:

            [x] --- [y]
           /
[a] --- [b] --- [c] --- [d]

здесь есть две головы в одном хранилище. Ваша рабочая копия всегда будет отражать один набор изменений, так называемый родительский набор изменений рабочей копии. Проверьте это:

% hg parents

предположим, что он сообщает [y]. Вы можете увидеть головы с

% hg heads

и в [y] и [d]. Если вы хотите обновить свой репозиторий до чистой проверки [d], тогда просто сделать (заменить [d] с номером редакции для [d]):

% hg update --clean [d]

вы увидите, что hg parents отчет [d]. Это означает, что ваш следующий коммит будет иметь [d] как родитель. Таким образом, вы можете исправить ошибку, которую заметили в главной ветке, и создать набор изменений [e]:

            [x] --- [y]
           /
[a] --- [b] --- [c] --- [d] --- [e]

нажать changeset [e] только, вам нужно сделать

% hg push -r [e]

здесь [e] - это хэш набора изменений. По умолчанию hg push будет просто сравните репозитории и посмотрите, что [x], [y] и [e] отсутствуют, но вы не можете поделиться [x] и [y] еще.

если исправление также влияет на вас, вы хотите объединить его с вашей веткой функций:

% hg update [y]
% hg merge

это оставит ваш график репозитория выглядеть следующим образом:

            [x] --- [y] ----------- [z]
           /                       /
[a] --- [b] --- [c] --- [d] --- [e]

здесь [z] - это слияние между [y] и [e]. Вы также могли бы выбрать, чтобы бросить ветку прочь:

% hg strip [x]

мой главный пункт этой истории таков: один клон может легко представлять несколько треков развития. Это всегда было верно для" plain hg " без использования каких-либо расширений. The расширение закладки это большая помощь, хотя. Это позволит вам назначать имена (закладки) наборам изменений. В приведенном выше случае вам понадобится закладка на вашей головке разработки и одна на восходящей головке. Закладки могут быть толкнул и потянул с Mercurial 1.6 и стали встроенной функцией в Mercurial 1.8.

если бы вы решили сделать два клона, ваш клон разработки выглядел бы так после создания [x] и [y]:

[a] --- [b] --- [x] --- [y]

и ваш восходящий клон будет содержать:

[a] --- [b] --- [c] --- [d]

теперь вы заметили ошибку и исправить ее. Здесь вам не придется hg update так как восходящий клон готов к использованию. Вы совершаете и создаете [e]:

[a] --- [b] --- [c] --- [d] --- [e]

чтобы включить исправление в свой клон разработки, вы тянете его туда:

[a] --- [b] --- [x] --- [y]
           \
            [c] --- [d] --- [e]

и слияния:

[a] --- [b] --- [x] --- [y] --- [z]
           \                   /
            [c] --- [d] --- [e]

график может выглядеть по-разному, но он имеет ту же структуру, и конечный результат тот же. Используя клонов, вы должны были сделать немного меньше ментальной бухгалтерии.

именованные ветви на самом деле не попали в картину здесь, потому что они довольно необязательны. Сам Mercurial был разработан с использованием двух клонов в течение многих лет, прежде чем мы переключились на использование именованных ветвей. Мы поддерживаем ветку под названием "stable" в дополнение к ветке "default" и делаем наши выпуски на основе ветви "stable". Вижу стандартные ветвления страница в вики для описания рекомендуемого рабочего процесса.


Я думаю, вы хотите всю историю в одном РЕПО. Создание краткосрочного РЕПО предназначено для краткосрочных экспериментов, а не для крупных событий, таких как релизы.

git функция, которую я бы очень хотел видеть в hg.

вы должны сделать и.

начните с принятого ответа от @Norman: используйте один репозиторий с одной именованной веткой на выпуск.

затем есть один клон для каждой ветви выпуска для создания и тестирования.

одно ключевое замечание заключается в том, что даже если вы используете несколько репозиториев, вы должны избегать использования transplant для перемещения наборов изменений между ними, потому что 1) он изменяет хэш, и 2) это может ввести ошибки, которые очень трудно обнаружить, когда есть конфликтующие изменения от изменений вы пересадки и конечной ветви. Вы хотите сделать обычное слияние вместо этого (и без предварительного: всегда визуально проверять слияние), что приведет к тому, что @mg сказал в конце своего ответа:

график может выглядеть по-разному, но он имеет ту же структуру, и конечный результат тот же.

более подробно, если вы используете несколько репозиториев, репозиторий" trunk " (или по умолчанию, main, развитие, что угодно) содержит все изменений в все репозитории. Каждый репозиторий выпуска / ветви-это просто одна ветвь в магистрали, все объединены так или иначе обратно в магистраль, пока вы не захотите оставить старый выпуск. Поэтому единственное реальное различие между этим основным РЕПО и единственным РЕПО в схеме именованной ветви-это просто то, названы ветви или нет.

это должно сделать очевидным, почему я сказал: "начните с одного РЕПО". Это единственное РЕПО-единственное место, где вам когда-либо нужно будет искать любой набор изменений в любом выпуске. Вы можете дополнительно пометить наборы изменений в ветвях выпуска для управления версиями. Это концептуально ясно и просто, и делает system admin проще, так как это единственное, что абсолютно должно быть доступно и восстанавливаться все время.

но тогда вам все равно нужно поддерживать один клон на ветку/выпуск, который вам нужно построить и протестировать. Это тривиально, как вы можете hg clone <main repo>#<branch> <branch repo>, а потом ... --7--> в репо ветви будет только тянуть новые наборы изменений на этой ветви (плюс наборы изменений предков на более ранних ветвях, которые были объединены).

Эта настройка лучше всего соответствует модели фиксации ядра linux один съемник (разве не приятно вести себя как Лорд Лайнус. В нашей компании мы называем роль интегратор), поскольку основное РЕПО-это единственное, что разработчикам нужно клонировать, а съемнику нужно тянуть. Содержание филиала repos находится чисто для управления выпуском и может быть полностью автоматизирован. Разработчикам никогда не нужно тянуть из/push в репозитории ветвей.


вот пример @mg, переделанный для этой настройки. Отправная точка:

[a] - [b]

сделайте именованную ветвь для версии выпуска, скажем, "1.0", когда вы доберетесь до alpha release. Исправлены ошибки фиксации на нем:

[a] - [b] ------------------ [m1]
         \                 /
          (1.0) - [x] - [y]

(1.0) не является реальным набором изменений, так как именованная ветвь не существует до фиксации. (Вы могли бы сделать тривиальный зафиксируйте, например, добавление тега, чтобы убедиться, что именованные ветви созданы правильно.)

слияние [m1] является ключом к этой настройке. В отличие от репозитория разработчика, где может быть неограниченное количество голов, вы не хотите иметь несколько голов в своем основном РЕПО (за исключением старой ветви мертвого выпуска, Как упоминалось ранее). Поэтому всякий раз, когда у вас есть новые наборы изменений в ветвях выпуска, вы должны немедленно объединить их в ветвь по умолчанию (или более позднюю ветвь выпуска). Это гарантирует, что любое исправление ошибки в одном выпуске также включено во все последующие выпуски.

тем временем разработка в ветке default продолжается к следующему выпуску:

          ------- [c] - [d]
         /
[a] - [b] ------------------ [m1]
         \                 /
          (1.0) - [x] - [y]

и, как обычно, вам нужно объединить две головки в ветке по умолчанию:

          ------- [c] - [d] -------
         /                         \
[a] - [b] ------------------ [m1] - [m2]
         \                 /
          (1.0) - [x] - [y]

и это клон ветки 1.0:

[a] - [b] - (1.0) - [x] - [y]

теперь это упражнение, чтобы добавить новую ветку. Если это 2.0, то он определенно будет ветвиться по умолчанию. Если это 1.1, вы можете выбрать филиал off 1.0 или по умолчанию. Независимо от этого, любой новый набор изменений на 1.0 должен быть сначала объединен со следующей веткой, а затем по умолчанию. Это может быть сделано автоматически, если нет конфликта, в результате, просто пустую слить.


Я надеюсь, что пример проясняет мои предыдущие моменты. Подводя итог, преимущества такого подхода таковы:

  1. единственный авторитетный репозиторий, содержащий полный набор изменений и историю версий.
  2. ясный и упрощенный отпуск управление.
  3. четкий и упрощенный рабочий процесс для разработчиков и интеграторов.
  4. облегчение итераций рабочего процесса (обзоры кода) и автоматизация (автоматическое пустое слияние).

обновить сам hg это: the главная РЕПО содержит стандартные и стабильные ветви, а также стабильный РЕПО является клоном стабильной ветви. Однако он не использует версионную ветвь в качестве тегов версии вдоль стабильной филиал достаточно хорош для целей управления релизами.


главное отличие, насколько я знаю, заключается в том, что вы уже заявили: именованные ветви находятся в одном репозитории. Названные ветви имеют все под рукой в одном месте. Отдельные РЕПО меньше и легко перемещаться. Причина, по которой есть две школы мысли об этом, заключается в том, что нет явного победителя. Какие бы аргументы ни были наиболее разумными для вас, вы, вероятно, должны пойти с ними, потому что, скорее всего, их окружение наиболее похоже на ваше.


Я думаю, что это явно прагматичное решение в зависимости от сложившейся ситуации, например, размер объекта/редизайн. Я думаю, что вилки действительно хороши для участников с еще не коммиттерскими ролями, чтобы присоединиться к команде разработчиков, доказав свою способность с пренебрежительными техническими накладными расходами.


Я бы посоветовал не использовать именованные ветви для версий. Для этого и существуют бирки. Названные ветви предназначены для длительных диверсий, таких как stable филиала.

Так почему бы просто не использовать теги? Основной пример:

  • развитие происходит на одной ветви
  • всякий раз, когда создается релиз, вы помечаете его соответствующим образом
  • развитие продолжается дальше
  • если у вас есть некоторые ошибки, чтобы исправить (или чего угодно) в определенный выпуск, вы просто обновляете его тег, вносите изменения и фиксируете

это создаст новую, неназванную голову на default бранч, он же. анонимная ветвь, которая отлично подходит для hg. Затем вы можете в любой момент объединить исправления ошибок в основной трек разработки. Нет необходимости в именованных ветвях.