Продолжаю писать про систему контроля версий Mercurial. В этой части начну речь про сложные операции с репозиториями, а именно - создание ветвей и работа с ними. Работа с ветвями разработки пользователям Subversion доставляет немало головной боли, поэтому многие из них, когда видят во всех статьях про Mercurial, что им чуть ли не каждый день придется мержить (merge) ветки сильно пугаются, и теряют всякое желаение переходить на него. Однако я постараюсь переубедить всех недоброжелателей и консерваторов, поскольку в Mercurial работа с ветвлениями является не намного более сложной операцией чем коммит.
Перед тем как продолжить разбираться с Mercurial поздравляю всех своих читателей с наступившим новым годом, желаю всяческих успехов в различных аспектах жизни, гармонии и добра во внутреннем мире, а также постоянного интеллектуального и духовного роста! С новым годом, уважаемые!
Итак, мы уже разбирали основы работы с репозиториями Mercurial в предыдущей части, и у нас сформировалось 2 готовых репозитория, сегодня мы продолжим взаимодействовать с ними и разберем, как организовать ветвление, и как потом с ним "бороться". В этой части мы рассмотрим самое частое ветвление в Mercurual, которое можно даже назвать "спонтанным".
Первым ветвлением, с которым столкнется команда работающаяя с Mercurial - это ветвление при пулле новых изменений. Ситуация возникает когда в вашем локальном репозитории имеются закоммиченные но не запушенные изменения, и один (а то и несколько) ваших соратников закоммитили и запушили в "центральный" репозиторий свои изменения. Попробуем смоделировать ситуацию на имеющихся у нас трех репозиториях, с которыми мы начинали работать во второй части. Напомню читателям, что у нас в обоих репозиториях сохранено по две ревизии, при этом оба репозитория были синхронизированы с "центральным". Попробуем внести в репозитории различные изменения и посмотреть что из этого выйдет.
Для начала создадим файл first.txt в первом репозитории, закоммитим его и запушим:
$ echo "new text to first.txt" > first.txt $ hg status ? first.txt $ hg add first.txt $ hg commit $ hg outgoing comparing with /home/mike/Repositories/newProject searching for changes changeset: 2:66c5686e355e tag: tip user: mike@mike-vbox date: Thu Jan 07 22:28:39 2010 +0300 summary: Коммит файла first.txt в первом репозитории $ hg push pushing to /home/mike/Repositories/newProject searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files
А теперь сэмулируем ситуацию, когда наш товарищ также внес изменения, отличающиеся от наших, и посмотрим как такая ситуация разруливается средствами Mercurial, ведь подобная ситуация в случае командной разработки будет достаточно частой. Для этого переместимся в имеющийся у нас второй репозиторий, создадим в нем новый файл, и посмотрим что будет:
$ echo "file created in second repository" > second.txt $ hg status ? second.txt $ hg add adding second.txt $ hg commit $ hg log changeset: 2:6872fa960507 tag: tip user: mike@mike-vbox date: Sun Jan 10 19:40:45 2010 +0300 summary: Файл second.txt создан во втором репозитории changeset: 1:270e49e72f4b user: mike@mike-notebook date: Fri Nov 27 10:39:35 2009 +0300 summary: Записан файл other.txt в другом репозитории changeset: 0:8fae369766e9 user: mike@mike-notebook date: Fri Nov 27 08:58:01 2009 +0300 summary: Файл readme.txt добавлен в репозиторий
Итак у нас уже имеется ситуация, когда в локалном репозитории и в удаленном отличаются "головы" разработки, то есть существуют две различные ревизии, производные от одной. В терминах любой системы контроля версий - это ветвление, пусть пока неявное, но скоро все тайное станет явным. Попробуем запушить имеющиеся ревизии в "центральный" репозиторий:
$ hg outgoing comparing with /home/mike/Repositories/newProject searching for changes changeset: 2:6872fa960507 tag: tip user: mike@mike-vbox date: Sun Jan 10 19:40:45 2010 +0300 summary: Файл second.txt создан во втором репозитории $ hg push pushing to /home/mike/Repositories/newProject searching for changes abort: push creates new remote heads! (did you forget to merge? use push -f to force)
Итак Mercurial нам запрещает пушить, говорит что пуш приведет к созданию новой головы в удаленном репозитории. И предлагает смержить репозитории. Ну чтож давайте это сделаем:
$ hg incoming comparing with /home/mike/Repositories/newProject searching for changes changeset: 2:66c5686e355e tag: tip user: mike@mike-vbox date: Thu Jan 07 22:28:39 2010 +0300 summary: Коммит файла first.txt в первом репозитории $ hg pull pulling from /home/mike/Repositories/newProject searching for changes adding changesets adding manifests adding file changes added 1 changesets with 1 changes to 1 files (+1 heads) (run 'hg heads' to see heads, 'hg merge' to merge)
Мы вытянули с "центрального" репозитория все имеющиеся изменения, и Mercuial нам сообщает, что в локальном репозитории теперь две "головы" которые требуют слияния (мержа от английского to merge). Можно даже попросить Mercurial показать некоторую картинку (используется дополнение graphlog о котором я ещё не писал, расширение есть в стандартной поставке):
$ hg glog o changeset: 3:66c5686e355e | tag: tip | parent: 1:270e49e72f4b | user: mike@mike-vbox | date: Thu Jan 07 22:28:39 2010 +0300 | summary: Коммит файла first.txt в первом репозитории | | @ changeset: 2:6872fa960507 |/ user: mike@mike-vbox | date: Sun Jan 10 19:40:45 2010 +0300 | summary: Файл second.txt создан во втором репозитории | o changeset: 1:270e49e72f4b | user: mike@mike-notebook | date: Fri Nov 27 10:39:35 2009 +0300 | summary: Записан файл other.txt в другом репозитории | o changeset: 0:8fae369766e9 user: mike@mike-notebook date: Fri Nov 27 08:58:01 2009 +0300 summary: Файл readme.txt добавлен в репозиторий
Наличие двух "голов" очевидно, и что-то с этим надо делать. Поскольку мы пока не планировали целенаправленно создавать две ветви разработки. Поэтому выполним слияние имеющихся ветвей:
$ hg merge 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) $ ls first.txt other.txt readme.txt second.txt
Итак, Mercurial, после нашей команды "hg merge" смержил рабочую копию и репозиторий, и напоминает нам, что эти изменения следовало бы закоммитить. Так сделаем это:
$ hg commit $ hg glog @ changeset: 4:6d6c634e2e20 |\ tag: tip | | parent: 2:6872fa960507 | | parent: 3:66c5686e355e | | user: mike@mike-vbox | | date: Sun Jan 10 20:34:21 2010 +0300 | | summary: Выполнен мерж двух веток | | | o changeset: 3:66c5686e355e | | parent: 1:270e49e72f4b | | user: mike@mike-vbox | | date: Thu Jan 07 22:28:39 2010 +0300 | | summary: Коммит файла first.txt в первом репозитории | | o | changeset: 2:6872fa960507 |/ user: mike@mike-vbox | date: Sun Jan 10 19:40:45 2010 +0300 | summary: Файл second.txt создан во втором репозитории | o changeset: 1:270e49e72f4b | user: mike@mike-notebook | date: Fri Nov 27 10:39:35 2009 +0300 | summary: Записан файл other.txt в другом репозитории | o changeset: 0:8fae369766e9 user: mike@mike-notebook date: Fri Nov 27 08:58:01 2009 +0300 summary: Файл readme.txt добавлен в репозиторий
На картинке которую нам показывает Mercurial неплохо видно что же именно происходило с репозиторием в течение этого, можно сказать урока. Также замечу, что у последней ревизии два "предка", в отличие от остальных. Вообще в Mercrurial у ревизии может быть не более двух предков, что вполне логично, и для меня очевидно. Отправим теперь изменения в "центральный" репозиторий, и посмотрим что же делать теперь с ними первому разработчику.
$ hg push pushing to /home/mike/Repositories/newProject searching for changes adding changesets adding manifests adding file changes added 2 changesets with 1 changes to 1 files
Теперь переместимся в каталог первого разработчика, и получим изменения и из центрального репозитория:
$ hg pull pulling from /home/mike/Repositories/newProject searching for changes adding changesets adding manifests adding file changes added 2 changesets with 1 changes to 2 files (run 'hg update' to get a working copy) $ hg update 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg glog @ changeset: 4:6d6c634e2e20 |\ tag: tip | | parent: 3:6872fa960507 | | parent: 2:66c5686e355e | | user: mike@mike-vbox | | date: Sun Jan 10 20:34:21 2010 +0300 | | summary: Выполнен мерж двух веток | | | o changeset: 3:6872fa960507 | | parent: 1:270e49e72f4b | | user: mike@mike-vbox | | date: Sun Jan 10 19:40:45 2010 +0300 | | summary: Файл second.txt создан во втором репозитории | | o | changeset: 2:66c5686e355e |/ user: mike@mike-vbox | date: Thu Jan 07 22:28:39 2010 +0300 | summary: Коммит файла first.txt в первом репозитории | o changeset: 1:270e49e72f4b | user: mike@mike-notebook | date: Fri Nov 27 10:39:35 2009 +0300 | summary: Записан файл other.txt в другом репозитории | o changeset: 0:8fae369766e9 user: mike@mike-notebook date: Fri Nov 27 08:58:01 2009 +0300 summary: Файл readme.txt добавлен в репозиторий
Как видно из вышеприведенного лога, все прошло без различных эксцессов и проблем. Что не может не радовать. И, как и в прошлый раз, мы в трех репозиториях получили идентичную ситуацию, несмотря на несколько более сложную исходную.
А теперь думаю самое главное - зачем же все это надо, ведь в SVN нет подобных проблем, да и мержить там не так часто... Ответ здесь очень простой - подобная модель взаимодействия с репозиторием провоцирует пользователя на частые коммиты, он не боится сломать код или репозиторий неудачным коммитом, не боится помешать другим пользователям и т.д. Коммит в Mercurial локален - пока вы не захотите отправить его в другой репозиторий, вся ветка коммитов останется у вас. К тому же слияние ревизий в Mercurial сделано намного проще и логичнее слияния ревизий в Subversion. Да, в поздних версиях и SVN научился более-менее нормально мержить, однако до Mercurial ему по прежнему далековато. И именно возможность частых локальных коммитов, в том числе когда у вас отстутствует подключение к интернету (а со мной такое случается нередко), меня так привлекла в Mercurial. Все остальные аспекты были на втором плане.
В заключении сделаю одно важное замечание. В случае командной разработки, у Mercurial есть одна особенность (назвать недостатком как-то язык не поворачивается): целочисленная нумерация ревизий может быть различна в различных репозиториях, поэтому не стоит использовать целочисленные номера для идентификации ревизий при общении внутри команды. Поскольку идентифиратором ревизии в Mercurial является SHA1-хеш, вероятность совпадения которого для различных ревизий крайне мала, то стоит использовать именно его. Причем как показывает практика различных пользователей Mercurial - вполне достаточно первых 4х символов этого самого хеша (когда вам перестанет хватать 4х символов - используйте 5 ;) ).
Спасибо, за статью доставило ) переходим с svn!
ОтветитьУдалитьДавно пора. После того как год попользуешься Mercurial понимаешь насколько же неудобен был Subversion.
ОтветитьУдалитьНе могу прийти к ясному пониманию идеологии mercurial в части локальных репозиториев. Зачем они вообще нужны? Репозиторий - это хранилище, в которое могут писать несколько человек и которое позволяет объединять изменения нескольких человек. Локальным же репозиторием никто кроме владельца локальной машины пользоваться как правило не будет (если не рассматривать какие-то извращенные схемы работы). Поэтому вообще не понял про написанное выше о том, как здорово, что можно делать частые локальные коммиты... А кому они вообще нужны? Их лучше вообще не делать, локальные коммиты. Локальный коммит - это сохранение файла в рабочей папке. Есть конечно еще одно применение репозитория - версионность. Т.е. даже работая одному, имея локальный репозиторий, я имею возможность откатиться на любые свои изменения, если делаю коммиты в репозиторий. Но ребята, я например работаю с NetBeans и там система абсолютно прозрачно для меня делает локальные версии изменяемых файлов. И я в любое время могу их посмотреть. Здесь по-моему всё должно работать точно также. Сохранил файл - тут же произошел его коммит в локальный репозиторий. Иначе это какой-то анонизм. Сохранил файл - закоммитил, сохранил - закоммитил... Или нужно постоянно думать: коммитить мне сделанные изменения, или не коммитить... ??? Получается, нужен какой-то плагин, который будет отслеживать изменения в рабочей папки и автоматом коммитить в локальный репозиторий? Или я чего-то недопонимаю?
ОтветитьУдалитьМне кажется вы чего-то недопонимаете. Коммит - это не просто "сохранить". Это значит, что вы фиксируете логически законченную часть изменений, к которой можно сделать комментарий, и которая действительно должна быть сохранена.
ОтветитьУдалитьЯ на этой неделе напишу пост, в чем же самая соль распределенных систем контроля версий. Так сказать суммирую двухлетний опыт работы.
Тот кто не понимает что такое и зачем нужны локальные коммиты - не программист или говнокодер. При работе над проектами некоторые таски, а особенно юзер-стори могут делаться несколько дней а то и пару недель. Зафигачить недоделанную юзер стори в центральное хранилище - это нонсенс. Внутри работы над юзер стори нужно делать локальные комиты, например по таскам. Если бы программисты писали с первого раза 100% рабочий код - тогда можно было поставить под сомнение бранчи и локальные коммиты. Спасибо автору за статьи и хотелось бы увидеть что-то подобное про UI расширения для работы а также узнать что изменилось за последнее время
ОтветитьУдалить