Named Branches vs Multiple Repositories
В настоящее время мы используем subversion на относительно большой базе кода. Каждый выпуск получает свою собственную ветку, и исправления выполняются для ствола и переносятся в ветки выпуска с использованием svnmerge.py
Я верю, что пришло время перейти к лучшему контролю над источниками, и я некоторое время играю с Mercurial.
Кажется, что есть две школы по управлению такой структурой релизов с помощью Mercurial. Либо каждый релиз получает свое собственное хранилище, и исправления вносятся в ветку релиза и помещаются в основную ветвь (и любые другие более новые ветви релиза.) ИЛИ с использованием именованных веток в одном репозитории (или нескольких совпадающих копиях).
В любом случае кажется, что я мог бы использовать что-то вроде пересадки на изменения вишенков для включения в ветки релиза.
Я прошу вас; Каковы относительные достоинства каждого подхода?
6 ответов
Самая большая разница в том, как имена ветвей записываются в истории. С именованными ветвями имя ветви внедряется в каждый набор изменений и, таким образом, станет неизменной частью истории. С клонами не будет постоянной записи о том, откуда произошла конкретная ревизия.
Это означает, что клоны отлично подходят для быстрых экспериментов, когда вы не хотите записывать имя ветви, а именованные ветви хороши для долгосрочных ветвей ("1.x", "2.x" и т. П.).
Также обратите внимание, что в одном хранилище можно легко разместить несколько легких ветвей в Mercurial. Такие ветки в репозитории могут быть добавлены в закладки, чтобы вы могли легко найти их снова. Допустим, вы клонировали хранилище компании, когда оно выглядело так:
[a] --- [b]
Вы взломать и сделать [x]
а также [y]
:
[a] --- [b] --- [x] --- [y]
Значит пока кто ставит [c]
а также [d]
в хранилище, поэтому, когда вы тянете, вы получаете график истории, как это:
[х] --- [у] / [a] --- [b] --- [c] --- [d]
Здесь есть две головы в одном хранилище. Ваша рабочая копия всегда будет отражать один набор изменений, так называемый набор изменений родительской рабочей копии. Проверьте это с:
% hg parents
Допустим, это сообщает [y]
, Вы можете увидеть головы с
% hg heads
и это сообщит [y]
а также [d]
, Если вы хотите обновить свой репозиторий до чистой проверки [d]
, то просто сделай (подставь [d]
с номером ревизии для [d]
):
% hg update --clean [d]
Вы увидите, что hg parents
доклад [d]
, Это означает, что ваш следующий коммит будет иметь [d]
как родитель. Таким образом, вы можете исправить ошибку, замеченную в основной ветке, и создать набор изменений [e]
:
[х] --- [у] / [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]
Чтобы включить исправление в свой клон разработки, нужно его туда вставить:
[а] --- [б] --- [х] --- [у] \ [c] --- [d] --- [e]
и объединить:
[a] --- [b] --- [x] --- [y] --- [z] \ / [c] --- [d] --- [e]
График может выглядеть иначе, но он имеет ту же структуру и конечный результат тот же. Используя клонов, вы должны были сделать немного меньше мысленного бухгалтерии.
Именованные ветви на самом деле здесь не фигурируют, потому что они не обязательны. Сам Mercurial был разработан с использованием двух клонов в течение многих лет, прежде чем мы переключились на использование именованных веток. Мы поддерживаем ветку, называемую "стабильной", в дополнение к ветке "по умолчанию" и делаем наши релизы на основе "стабильной" ветки. См. Стандартную страницу ветвления в вики для описания рекомендуемого рабочего процесса.
Я думаю, что вы хотите всю историю в одном репо. Порождение краткосрочного репо для краткосрочных экспериментов, а не крупных событий, таких как релизы.
Одно из разочарований Mercurial заключается в том, что, похоже, нет простого способа создать недолговечную ветку, поиграть с ней, отказаться от нее и собрать мусор. Ветви навсегда. Я сочувствую тому, что никогда не хочу отказываться от истории, но очень дешевые одноразовые ветки git
особенность, которую я действительно хотел бы видеть в hg
,
Вы должны сделать оба.
Начните с принятого ответа от @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 должен быть сначала объединен со следующей веткой, а затем по умолчанию. Это может быть сделано автоматически, если нет конфликта, что приводит к просто пустому слиянию.
Я надеюсь, что пример проясняет мои предыдущие пункты. Таким образом, преимущества этого подхода:
- Единый официальный репозиторий, который содержит полный набор изменений и историю версий.
- Четкое и упрощенное управление релизами.
- Четкий и упрощенный рабочий процесс для разработчиков и интеграторов.
- Облегчите итерации рабочего процесса (обзоры кода) и автоматизацию (автоматическое пустое слияние).
UPDATE hg делает это сам: основное репо содержит стандартные и стабильные ветки, а стабильное - это клон стабильных веток. Тем не менее, он не использует версионную ветвь, поскольку теги версий в стабильной ветке достаточно хороши для целей управления релизами.
Насколько я знаю, главное отличие состоит в том, что вы уже заявили: именованные разветвленные находятся в одном репозитории. Именованные ветки имеют все под рукой в одном месте. Отдельные репо меньше и их легко перемещать. Причина в том, что есть две мысли о том, что нет явного победителя. Аргументы любой стороны, которые имеют для вас наибольшее значение, вероятно, вам следует использовать, поскольку, скорее всего, их среда наиболее схожа с вашей.
Я думаю, что это определенно прагматичное решение в зависимости от текущей ситуации, например, размера функции / редизайна. Я думаю, что вилки действительно хороши для тех, кто вносит вклады, которые еще не являются коммиттерами, чтобы присоединиться к команде разработчиков, доказав свою способность с незначительными техническими затратами.
Я бы действительно советовал не использовать именованные ветви для версий. Это действительно то, для чего нужны теги. Именованные ветки предназначены для продолжительных диверсий, как stable
ветка.
Так почему бы просто не использовать теги? Основной пример:
- Развитие происходит на одной ветке
- Всякий раз, когда создается релиз, вы соответствующим образом помечаете его
- Развитие просто продолжается
- Если у вас есть ошибки, которые нужно исправить (или что-то еще) в определенной версии, вы просто обновляете ее тег, вносите изменения и фиксируете
Это создаст новую безымянную голову на default
филиал, ака. анонимная ветка, которая отлично подходит в рт. Затем вы можете в любой момент слить коммиты с исправлениями ошибок в основную дорожку разработки. Нет необходимости для названных веток.