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

130

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

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

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

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

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

Джеймс Эмертон
источник

Ответы:

129

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

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

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

Мартин Гайслер
источник
1
если бы набор изменений исходил от другого пользователя, это было бы записано, поэтому в использовании клонов нет ничего плохого. При продвижении новой функции часто неинтересно знать, что вы сделали это из отдельного репо. Существует также расширение localbranch, которое дает вам только локальную ветку. Полезно, когда клонирование репо связано с высокими затратами (время / пространство).
Йоханнес Рудольф
2
ссылаясь на: «клоны отлично подходят для быстрых экспериментов» - Нет, не годятся! Что делать, если у вас в репо несколько тысяч файлов? Клонирование займет много времени (в любое время более 1 минуты), а переключение веток займет мгновение (<1 секунды). По-прежнему использование именованных веток приведет к загрязнению журнала изменений. Разве это не тупик? Или я чего-то упускаю?
seler
Ладно селер; Похоже на модификацию его первоначального аргумента; Клоны хороши там, где накладные расходы на создание нескольких полных копий не важны для вас или когда вы можете использовать символические / жесткие ссылки hg для снижения стоимости отдельных локальных рабочих копий для каждой ветки.
Уоррен П.
@seler: вы совершенно правы, что клоны непрактичны, если код большой. Тогда закладки - это решение.
Мартин Гейслер,
29

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

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

Норман Рэмси
источник
20
Вы можете очень легко создать такую ​​функциональную ветку: «hg update» до точки ветвления, отредактировать и «hg commit». Вы только что создали расходящуюся линию развития - новые коммиты расширят эту ветвь. Используйте «hg clone -r», чтобы избавиться от него, или удалите его в строке с помощью «hg strip». Поэтому, пожалуйста, не разочаровывайтесь и не приходите в списки рассылки Mercurial со своими запросами о функциях.
Мартин Гейслер,
8
Похоже, hg stripэто то, что я хочу. Почему нельзя удалить ветки онлайн-документации?
Норман Рэмси,
11
См. Также этот пост в блоге для объяснения того, почему у Mercurial есть в некотором смысле более дешевые ветки, чем у git: stevelosh.com/blog/entry/2009/8/30/…
Мартин Гейслер,
9
Вы можете закрыть именованную ветку с помощью hg ci --close-branch.
Андрей Власовских
3
@Norman Ramsey: когда люди говорят, что ветки нельзя удалять, они имеют в виду, что вы не можете изменить имя ветки, встроенное в ревизии. Не набор изменений нам не на ветке, она определяет ветку. Вам нужно будет удалить набор изменений и воссоздать его с другим именем ветки, если вы хотите «переместить» его в другую ветку.
Мартин Гейслер
14

Вам следует сделать и то, и другое .

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

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

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

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

Более подробно, если вы используете несколько репозиториев, «основной» репозиторий (или по умолчанию, основной, для разработки, что угодно) содержит ВСЕ ревизии во ВСЕХ репозиториях. Каждый репозиторий релиза / ветки - это просто одна ветка в стволе, все они так или иначе объединены обратно в ствол, пока вы не захотите оставить старый выпуск. Следовательно, единственное реальное различие между этим основным репо и единым репо в схеме именованных ветвей состоит в том, имеют ли ветки имена или нет.

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

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

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


Вот пример @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. Упростите итерацию рабочего процесса (обзоры кода) и автоматизацию (автоматическое слияние пустых данных).

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

Джеффри Чжэн
источник
5

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

DWC
источник
2

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

thSoft
источник
0

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

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

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

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

DanMan
источник
Это во многом зависит от вашего процесса. Например, веб-приложение хорошо работает с иерархией ветвей стабильный / тестируемый / разрабатываемый. При создании программного обеспечения для настольных компьютеров у нас обычно есть ветка разработки (по умолчанию), а также от одной до трех (!) Различных веток в обслуживании. Трудно предсказать, когда нам может понадобиться повторно посетить ветку, и есть определенная элегантность в том, чтобы ветка отслеживала версию major.minor.
Джеймс Эмертон