Как Mercurial работает со многими разработчиками?
Я смотрю Mercurial-хранилища некоторых известных продуктов, например TortoiseHg и Python, и хотя я вижу, что несколько человек совершают изменения, временная шкала всегда выглядит довольно чистой, только одна ветвь движется вперед.
Однако скажите, что у вас 14 человек, работающих над одним и тем же продуктом, не скоро ли они попадут в ветхий кошмар с 14 параллельными ветвями в любой момент времени?
Например, только с двумя людьми и с продуктом в редакторе X теперь обе разработчики начинают работать над отдельными функциями в понедельник утром, поэтому оба начинаются с одного и того же родительского набора изменений.
Когда они фиксируют, теперь у нас есть две ветки, а затем с 14 людьми, у нас быстрая ветка 10+ (возможно, не 14...), которые должны быть объединены обратно в значение по умолчанию.
Или... Что я не вижу здесь? Возможно, это не проблема?
Изменить. Я вижу там некоторую путаницу в отношении того, о чем я действительно спрашиваю здесь, поэтому позвольте мне уточнить.
Я полностью и хорошо знаю, что Mercurial легко обрабатывает несколько ветвей и объединяется, и, как говорится в одном из ответов, даже когда люди работают над одними и теми же файлами, они не часто работают на одних и тех же строках, и даже тогда конфликт легко обрабатывается. Я также знаю, что если два человека создают адский ад, потому что они меняют много одного и того же кода в одних и тех же файлах, здесь наблюдается общая ошибка планирования, поскольку мы разместили две функции в одном и том же месте на двух разработчиков, вместо того, чтобы, возможно, пытаться их работать вместе или просто дать как одному разработчику в первую очередь.
Так что не это.
Мне интересно, как этот проект с открытым исходным кодом управляет такой чистой историей. Мне не важно (как один комментарий задавался вопросом), что история чиста, я имею в виду, что мы работаем параллельно, что репозиторий способен отражать это, тем лучше (на мой взгляд), однако эти хранилища я ' ve посмотрел, не имеет этого. Похоже, что они работают по модели Subversion, где вы не можете зафиксировать ее до того, как будете обновлены и объединены, и в этом случае история будет только одной прямой.
Итак, как они это делают?
Являются ли они "переустановкой" изменений, чтобы они, по-видимому, следовали за последним концом ветки, хотя первоначально они были немного исправлены в истории веток? Пересадить изменения, чтобы заставить их казаться "совершенными в основной ветке, чтобы начать?"
Или проекты, на которые я смотрел так медленно (на данный момент, я не смотрел далеко назад в истории), добавляя новые вещи, которые на самом деле они работали только один человек за раз?
Или они подталкивают изменения к одному центральному разработчику, который просматривает, а затем интегрирует? Это не похоже на то, что многие из проектов, на которые я смотрел, имели разные имена в наборах изменений.
Ответы
Ответ 1
В вашем обновленном вопросе кажется, что вас больше интересуют способы упорядочивания истории. Когда у вас есть история и вы хотите превратить ее в одну, аккуратную, прямую линию, вы хотите использовать rebase, transplant и/или меркурийные очереди. Проверьте эти документы для этих трех, и вы должны понимать рабочий процесс, как это делается.
Изменить. Поскольку Im ждет компиляции, здесь следует конкретный пример того, что я имею в виду:
> hg init
> echo test > a.txt
> hg addremove && hg commit -m "added a.txt"
> echo test > b.txt
> hg addremove && hg commit -m "added b.txt"
> hg update 0 # go back to initial revision
> echo test > c.txt
> hg addremove && hg commit -m "added c.txt"
Запуск hg glog
теперь показывает эту (расходящуюся) историю с двумя ветвями:
@ changeset: 2:c79893255a0f
| tag: tip
| parent: 0:7e1679006144
| user: mizipzor
| date: Mon Jul 05 12:20:37 2010 +0200
| summary: added c.txt
|
| o changeset: 1:74f6483b38f4
|/ user: mizipzor
| date: Mon Jul 05 12:20:07 2010 +0200
| summary: added b.txt
|
o changeset: 0:7e1679006144
user: mizipzor
date: Mon Jul 05 12:19:41 2010 +0200
summary: added a.txt
Сделайте rebase, сделав changeet 1 в дочерний элемент 2, а не 0:
> hg rebase -s 1 -d 2
Теперь снова проверьте историю:
@ changeset: 2:ea0c9a705a70
| tag: tip
| user: mizipzor
| date: Mon Jul 05 12:20:07 2010 +0200
| summary: added b.txt
|
o changeset: 1:c79893255a0f
| user: mizipzor
| date: Mon Jul 05 12:20:37 2010 +0200
| summary: added c.txt
|
o changeset: 0:7e1679006144
user: mizipzor
date: Mon Jul 05 12:19:41 2010 +0200
summary: added a.txt
Presto! Одна линия.:)
Также обратите внимание, что я не сделал слияние. Когда вы так переустановите, вам придется иметь дело с конфликтами слияния и все так же, как если бы вы сделали слияние. Потому что это в значительной степени то, что происходит под капотом. Поэкспериментируйте с этим в небольшом тестовом репо. Например, попробуйте изменить файл, добавленный в revision 0
, а не просто добавить больше файлов.
Ответ 2
Или... Что я не вижу здесь? Возможно, это не проблема?
Это не проблема. В большом проекте, даже когда люди работают с одной и той же функцией, они обычно не работают в одном файле. Когда они работают над одним и тем же файлом, они обычно не изменяют одни и те же строки. И когда они изменяют одни и те же строки, слияние должно выполняться вручную (для затронутых строк).
Это на практике означает, что 80%% слияний может быть сделано автоматически с помощью самого Mercurial.
Возьмем пример:
у вас есть:
[branch 1] [branch2]
\ /
\ /
[base]
Изменить: для ясности, с помощью ветки. Здесь я ссылаюсь на неназванные ветки.
Если у вас есть файл, измененный в branch 1
, но тот же файл в branch 2
такой же, как в base
, тогда выбирается версия в branch 1
. Если файл изменен как в branch 1
, так и в branch 2
, файлы объединяются по строкам одним и тем же алгоритмом: если строка 1 в файле1 в branch 1
отличается от строки 1 в файле1 в base
, но branch 2
и base
имеют линию 1 равную, выбирается строка 1 в branch 1
(и так далее и т.д.).
Для строк, которые были изменены в обоих ветвях, Mercurial прерывает процесс автоматического слияния и предлагает пользователю выбрать, какие строки использовать, или отредактировать строки вручную.
Так как решение о том, какие строки использовать, лучше всего делать человеку (ами), который изменил эти строки, хорошая практика заключается в том, чтобы человек, который реализовал функцию, выполнял слияние. Это означает, что если я и вы работаете над одним и тем же проектом, я реализую свою функцию, а затем вытаскиваю из центрального/общего репозитория (получаю последнюю версию, которую каждый использует), затем объединяет мою новую версию с вытащенными изменениями, а затем публикует это в общий репозиторий (на данный момент общий репозиторий имеет одну основную ветвь с моими сложенными изменениями). Затем вы извлекаете это из сервера и делаете то же самое с изменениями.
Это означает, что каждый может делать все, что захочет, в своем локальном репозитории, а общий/официальный репозиторий имеет одну ветвь. Это также означает, что вам необходимо определить временные рамки, когда люди должны объединить свои изменения.
У меня было три или четыре репозитория на моей машине, уже скомпилированные в разных версиях продукта (разные ветки репозитория) и несколько разных ветвей в моем основном хранилище (один для рефакторинга, один для разработки и т.д.). Всякий раз, когда я привожу одну ветку в стабильное состояние (скажем - завершаю рефакторинг), я бы вытащил с сервера, объединил эту ветвь в вытащенные изменения, а затем вернул ее на сервер и сообщил всем, что если они внесут какие-либо изменения в затронутые файлы, они должны вытащить сначала с сервера.
Мы использовали синхронизацию реализованных функций каждый понедельник утром, и нам потребовалось около часа, чтобы объединить все, а затем сделать еженедельную сборку на сервере, чтобы дать QA (в плохие дни потребуется два члена команды два часа или так что тогда все будут тянуть изменения недели на своей машине и использовать их в качестве новой базы на неделю). Это было для команды из восьми разработчиков.
Ответ 3
Я разработчик Mercurial, поэтому позвольте мне объяснить, как мы это делаем.
В проекте Mercurial мы принимаем вклады в виде патчей, отправленных в список рассылки. Когда мы применяем теги с hg import
, мы делаем неявный rebase на кончик ветки, над которой мы работаем. Это очень помогает в сохранении истории.
Что касается моих собственных изменений, я использую rebase или mq, чтобы линеаризовать вещи, прежде чем я их нажму, чтобы сохранить порядок в истории. Это в основном вопрос
hg push # abort: creates new remote head
hg pull
hg rebase
hg push
Вы можете комбинировать pull и rebase, если хотите (hg pull --rebase
), но мне всегда нравилось делать один шаг за раз.
Кстати, есть некоторые разногласия по поводу этой практики линеаризации истории - некоторые считают, что история должна показать, как все происходит на самом деле, со всеми ветвями и слияниями и еще много чего. Я нахожу, что до тех пор, пока вы не возитесь с общедоступными наборами изменений, тогда все в порядке и полезно линеаризовать историю.
Ответ 4
Ядро Linux хранится в тысячах хранилищ и, вероятно, в миллионах ветвей, и это, похоже, не создает проблемы. Для крупных проектов вам нужна стратегия репозитория (например, стратегия диктатора-лейтенанта), но наличие многих веток является основной силой современных DVCS, а не проблемой вообще.
Ответ 5
Да, нам нужно будет объединиться и избежать глав в основном репозитории, слияние должно выполняться разработчиками в дочерних репозиториях.
Итак, прежде чем вы нажмете свой код в родительский репозиторий, вы сначала потянете последние изменения, слейте на свою сторону и (попробуйте) нажать. Это должно избегать нежелательных головок в мастер-репо
Ответ 6
Я не знаю, как команда TortoiseHg делает что-то, но вы можете использовать Mercurial расширение для переадресации для "отсоединения" ветки и отбрасывания это на вершине наконечника, создавая одну ветвь.
На практике, однако, я не беспокоюсь о нескольких ветвях, если не вижу больше голов, чем должно быть. Слияние не очень большое дело.