Системы контроля версий. Централизованная система контроля версий Subversion и децентрализованная система контроля версий Mercurial. Основные принципы, отличие, приёмы. Материалы к семинару 3 сентября 2012.
При совместной работе нескольких разработчиков над одним программным продуктом возникает проблема совместного доступа к исходным кодам и ресурсам, а также протоколирование, кто чего когда правил – для поиска виноватых и возможности отката на более ранние версии, разумеется.Для того, чтоб это дело отслеживать и хранить, придуманы системы контроля версий. Существуют СКВ централизованные, в которых имеется один репозиторий, в который собираются изменения со всех рабочих копий разработчиков, и децентралзованные, когда репозиториев много, и они могут обмениваться изменениями между собой.
Начну с централизованных СКВ, так как исторически они появились раньше. Тут всё просто. Репозиторий один. У каждого разработчика своя рабочая копия. Время от времени разработчик может затягивать к себе в рабочую копию новые изменения из репозитория, или проталкивать свои изменения из своей рабочей копии в репозиторий. У подобной системы существует один большой недостаток – если похерится репозиторий, то всё пропадает. Прочие особенности централизованных СКВ зависят от реализации.
Subversion 1.6 и 1.7
Начну, пожалуй, с упоминания того момента, что из-за того, что в версии 1.7.0 была полностью исправлена система хранения метаданных в хранилище, рабочие копии версий веток 1.6 и 1.7 между собой несовместимы. Это не представляет особых проблем, так как формат связи с репозиторием не изменился, и работать можно с одним и тем же репозиторием из рабочих копий версий 1.6 и 1.7 можно одновременно, и 1.6 легко апгрейдится до 1.7 самостоятельно.Репозиторий может храниться в базе данных BerkleyDB (с 2001 года примерно) или в файловой системе в своём особом формате (с 2004 где-то). Оба этих способа имеют очевидные преимущества и недостатки. Например, при хранении в файловой системе SVN не хранит отдельно ревизию HEAD. А так как всё хранится в виде дельт (наборов изменений в файлах), то получение последней ревизии может занимать много времени. Зато работает в общем случае быстрее и занимает меньше места, чем БД.
Рабочая копия представляет из себя папки и файлы в таком виде, в котором они существуют в самом проекте, только в корне рабочей копии лежит папка с названием .svn, в которой хранится вся метаинформация. Раньше, в версии 1.6 и более древних, такие папки были рассованы повсеместно, в каждой директории.
В том случае, если вам необходимо игнорировать неверсифиционированные элементы (как, например, в случае с битриксом, когда один проект затрагивает публичную часть, модуль, компоненты, шаблоны, но не хранит все исходники битрикса), то можно воспользоваться двумя способами. Первый – это задать в ini-файле глобальные параметры игнорирования. Однако, они применимы только для данного пользователя и/или данного компьютера, так как ini-файлы у всех разные и не привязаны к репозиториям. Кроме того, мне неизвестно, каким образом можно задавать параметры игнорирования для разных рабочих копий одним пользователем, возможно, что никак. Второй способ - через свойства. В SVN у каждой папки и файла есть определённые метаданные, так называемые «свойства». Есть свойство svn:ignore, в котором указываются игнорируемые файлы по маске. Для того, чтоб заставить svn игнорировать очень сложный набор файлов – таких, как исходники битрикса в наших проектах, например – придётся здорово попотеть. Так как эти свойства хранятся хитрым образом закопанные в папке .svn, то они, похоже, намертво пришиты к своей рабочей копии, и копирование их в другую рабочую копию представляется мне маловероятным. Кроме того, есть свойство svn:mime-type, которое определяет тип содержимого файла. Если svn неверно угадывает тип файла, например, принимает бинарный файл за текстовый и пытается его версионировать, или наоборот, принимая текстовый файл за двоичный и перезаписывая его при изменении, вместо того, чтоб провести слияние, то, указав правильный тип в этом свойстве, можно исправить эту оплошность.
Ветви в subversion устроены особым образом. Их там по сути нет. То есть даже в документации по SVN написано, что надо сделать отдельный каталог branches и сделать svn copy туда для создания ветки. Точно таким же образом устроены метки (тэги) – сделать отдельную папку tags и делать svn copy туда. Этот деревянный способ, однако, проще для понимания, чем аналогичные штуки в система контроля версий Mercurial. К которой я и перехожу.
Mercurial
Это децентрализованная система управления версиями. Может быть, чуть менее или чуть более распространённая, чем всем известный Git, но более лёгкая в освоении, хотя не менее мощная, за исключением, может быть, некоторых тонкостей профессионального использования, которые, впрочем, решаются подключением соответствующих плагинов.Для людей, привыкших к централизованным системам контроля версий, децентрализованные системы являются жутчайшей ересью, и они просто не понимают, каким образом там вообще можно что-то делать. Однако, любая децентрализованная система является более гибкой и надёжной просто за счёт того, что она децентрализованная. Тут тоже есть понятие «репозиторий» и «рабочая копия», однако, у каждого репозитория может быть только одна рабочая копия, представляющая с ним единое неделимое целое, и таких репозиториев-рабочих-копий у одного проекта может быть неограниченное число. При этом репоизтории могут обмениваться между собой изменениями.
Hg хранит правки в репозиториях тоже в виде дельт, или изменений. Изменение (вернее, набор изменений - changeset) – это указание, что поменялось, и указание на изменение-родитель (или два изменения-родителя при слиянии). Это как односвязный список, знакомый всем их лабораторных работ по C++. У нас есть один элемент, мы можем отследить всех его предков и собрать по отдельным изменениям один большой файл. Такая архитектура хранения изменений практически ничем не ограничена, и мы можем наколбасить огромную кучу разных равноправных изменений одной ревизии, а потом снова собрать их попарно в одно в пределах одного репозитория.
Причём это не что-то экзотическое, а вполне нормальный рабочий процесс. Следующие цепочкой один за другим наборы изменений вполне себе могут ветвиться. Разные разработчики вносят правки, сохраняют у себя в репозиториях. Потом, когда в одном репозитории все эти правки объединяются, возникает как раз такая ситуация, когда от одного набора изменений расходятся несколько. Это так называемые отдельные головы. Так как это происходит постоянно, то постоянно необходимо использовать hg merge, который слепляет все головы в одну, создавая новые наборы изменений, имеющих по паре родителей. Если есть необходимость держать разные наборы изменений параллельно друг другу и изолированно, то одну голову можно просто обозвать каким-то именем – это получается ветка. Свою рабочую копию можно легко переключать между ветками командой hg up. Причём перенос изменений из любых веток в основную ветвь, default, очень легок и делается всё той же командой hg merge.
Дальше пойдёт обсирание SVN и централизованных СКВ, поэтому людей с централизованным образом мышления я прошу покинуть аудиторию во избежание баттхёрта.
Работая с меркуриалом, вы сначала затягиваете откуда-то, скажем, из репозитория, выбранного как центральный, изменения. Потом сливаете их со своими. Потом только проталкиваете обратно. Поэтому вы всегда можете проверить, как что слилось, перед тем, как проталкивать свои изменения обратно. Поэтому количество факапов обычно чуть ниже, чем при работе с SVN, когда вы напрямую коммитите сразу в репозиторий.
Так как в Hg все репозитории равноправны, то, если даже взорвётся датацентр, где все ваши центральные репозитории, и сгорят все сервера со всеми жёсткими дисками, вы можете, во-первых, продолжать работать, делая коммиты в свои локальные репозитории, во-вторых, обмениваясь изменениями напрямую между собой, безо всякого центрального репозитория. Новый центральный репозиторий можно склонировать из любого имеющегося и снова собрать там все изменения в течение нескольких минут.
Аналогично – при отсутствии связи (сломался интернет или вы решили поработать в глухой тайге), вы можете фиксировать изменения в своём локальном репозитории, делая в него коммиты, чтоб потом объединить свои изменения с изменениями других разработчиков, как только снова появится связь.
В том случае, если вам надо изолировать часть изменений, вы элегантно делаете новую ветку, просто помечая ряд наборов изменений как новую ветвь. Ничего не надо никуда копировать, никаких костылей. Точно так же, легко и элегантно, вы можете собирать в ветку изменения из других ветвей, и выгружать изменения из неё, когда они будут готовы. При работе в SVN, насколько мне известно, многие разработчики стараются избегать ветвей, хм, интересно, почему бы это?
Проблема с игнорированием неверсионируемых файлов решается так же элегантно. Помимо типично SVN-style подхода с ini-файлом (который может быть полезен пользователям операционных систем Mac OS или Windows для того, чтоб не сохранять в репозитории файлы .DS_Stote и Thumbs.db), существует ещё один простой способ – в корне рабочей папки создаётся файл .hgignore, в котором перечисляются по маскам файлы, которые нужно игнорировать. Этот файл можно просто скопировать в другую рабочую копию, и он там будет точно так же нормально работать.
Основное, фундаментальное различие между SVN и Hg в том, что в SVN все мыслят прямолинейно и плоско ревизиями, а в Hg – наборами изменений. Ревизия – это то, на что похожа файловая система в определённый момент времени. Поэтому там такая жопа с ветвями и тэгами. Набор изменений – это всего лишь набор изменений. На него можно навесить тэг, пометить, как отдельную ветвь, и так далее. Поэтому СКВ Hg более гибка и более мощна. Что касается небольших преимущество в комфорте при работе из командной строки, то, я думаю, особого смысла описывать их нет, хотя упомянуть стоит.
Ну вот, наверное, собственно, и всё.
Комментариев нет:
Отправить комментарий
Ублюдочный Гугл поломал форму комментариев. Извините.
Примечание. Отправлять комментарии могут только участники этого блога.