Именованные ветви против нескольких хранилищ - PullRequest
130 голосов
/ 21 мая 2009

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

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

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

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

Я прошу вас; Каковы относительные достоинства каждого подхода?

Ответы [ 6 ]

129 голосов
/ 21 мая 2009

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

Это означает, что клоны отлично подходят для быстрых экспериментов, когда вы не хотите записывать имя ветви, а именованные ветви хороши для долгосрочных ветвей ("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]

Чтобы нажать только ревизию [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]

Моя главная мысль этой истории такова: один клон может легко представлять несколько путей развития. Это всегда было верно для "обычного hg" без использования каких-либо расширений. расширение закладок очень помогает. Это позволит вам назначать имена (закладки) для наборов изменений. В приведенном выше случае вам понадобится закладка на вашей голове разработки и одна на верхнем. Закладки могут быть выдвинуты и вытянуты с 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 был разработан с использованием двух клонов в течение многих лет, прежде чем мы переключились на использование именованных веток. Мы поддерживаем ветку, называемую «стабильной», в дополнение к ветке «по умолчанию» и делаем наши релизы на основе «стабильной» ветки. См. Стандартную ветвь в вики для описания рекомендуемого рабочего процесса.

29 голосов
/ 21 мая 2009

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

Одним из разочарований Mercurial является то, что, похоже, нет простого способа создать недолговечную ветку, поиграть с ней, отказаться от нее и собрать мусор. Ветви навсегда. Я сочувствую тому, что никогда не хочу отказываться от истории, но супердешевые одноразовые ветки - это функция git, которую я действительно хотел бы увидеть в hg.

14 голосов
/ 21 сентября 2010

Вы должны сделать оба .

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

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

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

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

Более подробно, если вы используете несколько репозиториев, репозиторий "trunk" (или по умолчанию, main, development и т. Д.) Содержит ALL наборов изменений в ALL репозиториях. Каждый репозиторий выпусков / ветвей - это просто одна ветвь в стволе, все объединенные обратно тем или иным образом в ствол, пока вы не захотите оставить старый выпуск. Следовательно, единственное реальное различие между этим основным репо и единственным репо в схеме именованных ветвей заключается просто в том, являются ли ветви именованными или нет.

Это должно прояснить, почему я сказал «начать с одного репо». Это единственное хранилище - единственное место, где вам когда-либо нужно будет искать любые изменения в любом выпуске . Вы также можете пометить наборы изменений в ветвях релиза для управления версиями. Это концептуально ясно и просто, и делает системного администратора проще, поскольку это единственная вещь, которая обязательно должна быть доступна и восстанавливаться постоянно.

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

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


Вот пример @ mg, переработанный для этой настройки. Начальная точка:

[a] - [b]

Создайте именованную ветку для версии выпуска, скажем «1.0», когда вы перейдете к альфа-версии. Зафиксируйте исправления:

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

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

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

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

          ------- [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, вы можете выбрать ответвление 1.0 или значение по умолчанию. В любом случае любой новый набор изменений в версии 1.0 должен быть сначала объединен со следующей веткой, а затем по умолчанию. Это может быть сделано автоматически, если нет конфликта, что приводит к просто пустому слиянию.


Надеюсь, этот пример прояснит мои предыдущие моменты. Таким образом, преимущества этого подхода:

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

ОБНОВЛЕНИЕ hg само по себе делает это : основное репо содержит стандартные и стабильные ветви, а стабильное репо является стабильным клоном ветви. Тем не менее, он не использует версионную ветвь, поскольку метки версий в стабильной ветке достаточно хороши для целей управления релизами.

5 голосов
/ 21 мая 2009

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

2 голосов
/ 19 ноября 2009

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

0 голосов
/ 09 августа 2014

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

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

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

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

...