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

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

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

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

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

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

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

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

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

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

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

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

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

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

Ответ 4

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

Ответ 5

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

Ответ 6

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

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

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

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