суббота, 27 ноября 2010 г.

Используем reCaptcha в приложениях django

Progg it

Сегодня мы поговорим об использовании reCaptcha в django-приложениях. Не буду говорить о достоинствах reCaptcha, думаю, что они и так понятны. Итак, наша задача состоит в том, чтобы как можно меньшими усилиями прикрутить reCaptcha к нашему приложению. Для этого нам потребуется выполнить следующее:

  • Зарегистрироваться здесь и получить наши ключи
  • Сделать приложение django, которое будет отвечать за взаимодействие с серверами reCaptcha, и позволит использовать ее в наших формах
  • Включить ее в одну из форм приложения, где она требуется

Шаг 1. Регистрация

Думаю, что как регистрироваться может разобраться любой из тех, кто понял что ему нужна reCaptcha в разрабатываемом приложении. Главное получить и сохранить ключи для вашего приложения. В зависимости от вашего желания вы можете получить ключи на конкретный адрес, или на все. Отмечу сразу, что любая пара валидных ключей корректно работает с адреса 127.0.0.1, что позволяет писать и отлаживать приложение на локальной машине, и не огребать при этом лишних проблем. Полученные ключи пропишем в settings.py примерно таким образом:

  ...
  #reCAPTCHA keys  
  RECAPTCHA_PUBLIC_KEY = < ПУБЛИЧНЫЙ КЛЮЧ > 
  RECAPTCHA_PRIVATE_KEY = < СЕКРЕТНЫЙ КЛЮЧ >
  ... 

На этом подготовительный этап закончен.

Шаг 2. Приложение django

Создадим приложение django, которое будет предоставлять функциональность другим приложениям и скрывать детали взаимодействия с сервисом reCaptcha. Скажу честно, что я взял приложение marcofucci, и только слегка его модифицировал.

Для начала необходимо скачать recaptcha-client. При этом нам потребуется только captcha.py из клиента. Берем этот файлик и кладем в наше приложение. Он обеспечит нам базовое взаимодействие с сервисом. Теперь нам необходимо сделать виджет и поле формы, для того, чтобы можно было легко использовать reCaptcha в нашем приложении. Приведу сразу код, который взят у marcofucci:

widgets.py

  1:  # -*- coding: utf-8 -*-  
  2:  from django import forms  
  3:  from django.utils.safestring import mark_safe  
  4:  from django.conf import settings  
  5:  from recaptcha import captcha  
  6:  class ReCaptcha(forms.widgets.Widget):  
  7:    recaptcha_challenge_name = 'recaptcha_challenge_field'  
  8:    recaptcha_response_name = 'recaptcha_response_field'  
  9:    def render(self, name, value, attrs=None):  
  10:      return mark_safe(u'%s' % captcha.displayhtml(settings.RECAPTCHA_PUBLIC_KEY))  
  11:    def value_from_datadict(self, data, files, name):  
  12:      return [data.get(self.recaptcha_challenge_name, None),   
  13:        data.get(self.recaptcha_response_name, None)]   

forms.py

  1:  # -*- coding: utf-8 -*-  
  2:  from django.conf import settings  
  3:  from django import forms  
  4:  from django.utils.encoding import smart_unicode 
  5:  from django.utils.translation import ugettext_lazy as _  
  6:  from recaptcha.widgets import ReCaptcha  
  7:  from recaptcha import captcha  
  8:  class ReCaptchaField(forms.CharField):  
  9:    default_error_messages = {  
  10:      'captcha_invalid': _(u'Invalid captcha')  
  11:    }  
  12:    def __init__(self, *args, **kwargs):  
  13:      self.widget = ReCaptcha  
  14:      self.required = True  
  15:      super(ReCaptchaField, self).__init__(*args, **kwargs)  
  16:    def clean(self, values):  
  17:      super(ReCaptchaField, self).clean(values[1])  
  18:      recaptcha_challenge_value = smart_unicode(values[0])  
  19:      recaptcha_response_value = smart_unicode(values[1])  
  20:      check_captcha = captcha.submit(recaptcha_challenge_value,   
  21:        recaptcha_response_value, settings.RECAPTCHA_PRIVATE_KEY, {})  
  22:      if not check_captcha.is_valid:  
  23:        raise forms.util.ValidationError(self.error_messages['captcha_invalid'])  
  24:      return values[0] 

Теперь мы готовы к использованию reCaptcha в нашем приложении.

Шаг 3. Использование

Использование сводится к созданию в форме соответствующего поля, и вполне обычной обработки формы в стиле django. Напиример так:

  1:  class RegistrationForm(ModelForm):  
  2:       recaptcha = ReCaptchaField(error_messages = {  
  3:            'required': u'Это поле должно быть заполнено',            
  4:            'invalid' : u'Указанное значение было неверно'  
  5:            })  
  6:       class Meta:  
  7:            model = UserData  
  8:            fields = (  
  9:                 'email',  
  10:                 'fio',  
  11:                 'phone',  
  12:                 'company',  
  13:                 'address'  
  14:            )  

В данном случае у меня имеется форма по модели, к которой добавлено поле с reCaptcha. Использование формы в функции вида приводить не буду, так как оно ничем не отличается от стандартного, достаточно заглянуть в документацию

суббота, 20 марта 2010 г.

Джоел и Subversion, Mercurial, Git. Действительно антибиотик?

Progg it
Недавно в блоге Джоела Спольски была опубликована последняя статья – «Distributed Version Control is here to stay, baby», по крайнее мере сам Джоель называет её последней. Статья, как понятно из заглавия посвящена распределенным системам контроля версий. После прочтения статьи возникло желание немного её прокомментировать. Сначала я выложу краткий обзор содержания, при этом постараюсь не упустить важных моментов. Делать полноценный перевод нет ни времени, ни желании. Итак, Джоел пишет следующее…
В распределенных системах контроля версий их распределенность не является самой интересной особенностью. Наиболее интересным является изменение модели – распределенные системы контроля версий работают с изменениями (changes), а не с версиями. Если централизованная система контроля версий «думает»: у меня есть версия 1, после этого будет версия 2, после этого версия 3 и так далее. В распределенной системе все по другому: сначала не было ничего, потом добавлены эти изменения, потом добавлены те, и т.д. Изменение программной модели должно изменить модель пользователя. Теперь пользователю тоже придется мыслить в терминах изменений. Если раньше было: «Я хочу получить версию номер Х», или «Я хочу последнюю версию», то теперь: «Хочу получить набор изменений Пети».
Это смена парадигмы! Именно поэтому, когда вы переходите с Subversion на Mercurial, у вас вроде бы все получается, но вас новая система все время расстраивает, вы не можете к ней привыкнуть, и начинаете её ненавидеть. Только когда вы начнете мыслить в терминах «изменении», и выбросите из головы «версии» все встанет на свои места. Именно изменение модели работы системы контроля версий привело к существенному упрощению слияния (merge) кода. И соответственно к более активному использованию ветвления, использованию его там, где оно необходимо. Теперь можно не думая о сложностях последующего слияния создавать долгоживущие ветви для команд тестирования и поддержки и создавать короткоживущие ветви для экспериментов.
Если вы все еще используете SVN – перестаньте. Mercurial и Git – это антибиотики. Теперь существуют технология лучше.
И, вы знаете, я с Джоелом согласен почти полностью. Джоел смог объяснить то, что понимает каждый, кто освоился в распределенных системах. Но не каждый сможет выразить это словами. Это действительно смена основной концепции системы контроля версий, и именно из-за этого DVCS так туго входят в коллективы программистов. Наблюдаю это на личном опыте. Все привыкли мыслить категориями Subversion, и как только человек видит новую ветку в репозитории, даже ту, которая по идее скоро сольется с основной – он почему то пугается.
Продолжая пугаться, и не совсем понимая основных принципов системы, человек продолжает делать коммиты «раз в сутки», вместо того, чтобы делать коммиты по необходимости, по завершению каждой связной частички.
Однако распределенные системы контроля версий – это отнюдь не антибиотики. Главная проблема, как и всегда, в головах. И без решения этой проблемы никакой Hg, Git или Bazaar не спасет. Да, они гораздо больше соответствуют workflow разработки ПО, и большинство действий в этих системах легко объясняется логической необходимостью и разумностью. Но способов выстрелить себе в ногу в них значительно больше, хотя они и не очевидны. Наблюдал однажды картину, когда 4 разработчика постоянно работали каждый в своей ветке – в этом случае никакая смена парадигмы контроля версий не поможет.
Распределенные системы контроля версий – это отличная вещь, это действительно шаг вперед, который открывает каждому разработчику новые возможности и новую функциональность. И это действительно происходит за счет смены модели взаимодействия, Джоэл, безусловно, прав. Но, мне кажется, воспринимать их как серебряную пулю неправильно. Они избавляют от значительной части головной боли связанной с контролем версий, ветвлениями и слиянием. Однако от кривых рук они все равно не вылечат. Поэтому все равно необходимо нормально разбивать задачи, думать головой, и мержиться не раз в месяц с десятком изменений в каждом файле от каждого автора. Тут уж никакой Mercurial не спасет.

вторник, 9 марта 2010 г.

Известные баги System.Net.Mail.SmtpClient в .NET 3.5

Progg it
Сегодня открыл для себя некоторые глюки класса System.Net.Mail.SmtpClient в .net framework 3.5. (Я уже не говорю что творилось с System.Web.Mail.*, но оно уже obsolete и слава небесам. RIP).
1. Некорректная реализация команды EHLO протокола SMTP. Согласно RFC#821 ещё лохматого года необходимо передавать FQDN хоста-отправителя, причем по RFC это правило строгое. Вместо этого в MS решили, что хватит и NetBIOS-имени компа. Соответственно сервера, не отклоняющиеся от стандарта посылают ентот SmtpClient лесом, как пытающийся разослать спам. Решения нет. Есть только очень неочевидный WorkAround через Reflection.
2. Вытекает из первого. MS хотела как лучше, и разрешила называть компьютеры в NetBIOS сетях именами с символами из национальных алфавитов. Мало того, собсно Windows 7 RU предлагает подобное имя при установке! (Правда при попытке сменить руками, уже после установки, честно предупреждает что нехорошо использовать символы русского алфавита). И, как я уже говорил выше, это NetBIOS-имя использует SmtpClient в качестве имени хоста, правда никак не кодируя символы национальных алфавитов. После чего сам же валится с исключением "недопустимый знак в заголовке электронной почты". Собственно исключение то другое, но вот InnerException именно такой. Решения нет. Есть только очень неочевидный WorkAround через Reflection.
3. Очень интересный взгляд на стандарты почтовых протоколов со стороны MS. Все уже привыкли, что MS если и читает чужие стандарты, то как то очень избирательно. Вот и стандарт на SMTP также ими был прочитан. И, соответственно, также реализован. Что привело к тому, что некоторые SMTP сервера напрочь не работают с SmtpClient от MS. Например Kerio. Решения нет. WorkAround'ов нет.
Обиднее всего то, что все это хозяйство тянется ещё с .net 2.0, все эти баги давно зарегистрированы, но MS плевало на отправку почты. Надо бы их SmtpClient с hotmail'ом проверить. Интересно, заработает ли?

воскресенье, 31 января 2010 г.

Введение в Mercurial. Часть 4. Способы организации ветвей

Progg it

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

Для начала дадим определения ветви, чтобы начинать с единого понимания процесса. Обратившись к wiki меркуриала я нашел определение, которое мне кажется вполне корректным: ветвь (branch) - это связанная последовательность ревизий (changeset) являющаяся отдельным направлением разработки. Таким образом, ветвь - это в первую очередь логическое понятие, так как в случае с распределенными системами контроля версий она будет содержать значительное число "спонтанных" ветвлений-слияний.

Исходное состояние

Рисунок 1. Исходное состояние репозитория

Анонимная ветвь

Рисунок 2. Новая анонимная ветвь в репозитории

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

$ hg branches
default                        4:6d6c634e2e20

Команда hg branches выводит список всех именованных ветвей в репозитории. Как мы видим основная ветвь разработки называется default. Если быть точным, так называется ветвь в которую происходит первый коммит в репозиторий, так сказать название по умолчанию. На рисунке 1 приведено текущее состояние репозитория и граф ревизий в нем находящихся. Я буду красным кружком отмечать "вершину" (tip) репозитория, как сказано в документации Mecurial, вершина - это самая свежая ревизия в репозитории. Команда hg branches не выводит анонимные ветви, хотя разработчики могут их использовать при необходимости, и создавать самостоятельно.

Анонимное ветвление

На самом деле, создание ветви при работе с Mercurial не является чем-то из ряда вон выходящим, для распределенной модели контроля версий это стандартная операция, и поэтому может быть произведена крайне просто, фактически, простым коммитом. То есть мы должны привести рабочую копию в состояние отличное от "вершины" (tip), внести изменения и выполнить коммит. Ничего больше. Для этого локальную копию исходного кода вернем в состояние ревизии 2:66c5686e355e:

$ hg update -r 66c5
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ ls -l                   
-rw-r--r-- 1 mike mike 22 2010-01-07 22:22 first.txt         
-rw-r--r-- 1 mike mike 61 2009-11-27 11:15 other.txt         
-rw-r--r-- 1 mike mike 57 2009-11-27 00:03 readme.txt    

После этого создадим в локальной копии файл branch.txt, добавим его в репозиторий и выполним коммит:

$ ls -l
-rw-r--r-- 1 mike mike 61 2010-01-31 18:55 branch.txt
-rw-r--r-- 1 mike mike 22 2010-01-07 22:22 first.txt 
-rw-r--r-- 1 mike mike 61 2009-11-27 11:15 other.txt
-rw-r--r-- 1 mike mike 57 2009-11-27 00:03 readme.txt
$ hg add
adding branch.txt
$ hg commit
created new head
$ hg branches
default                        5:ff8ffd5270cb

И Mercurial нам честно сообщает что создал новую "голову". На рисунке 2 показано текущее состояние репозитория. Отмечу два момента: во-первых, ветвь default осталась на своем месте, и теперь заканчивается ревизией 5:ff8ffd5270cb, то есть "вершиной" (tip); а во-вторых все эти ветвления находятся локально в нашем репозитории. Локальность производимых ветвлений - это главное, коренное, отличие от централизованных систем контроля версий, в том числе от Subversion. Никто не увидит вашей ветви до тех пор пока вы не синхронизируете свой репозиторий с удаленным (обычно командой push). С другой стороны выполнение pull приведет к появлению в вашем репозитории всех ветвей, имеющихся в удаленном.

Преимущества

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

Недостатки

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

Таким образом, анонимные ветви - это отличный механизм для внесения быстрых (по количеству ревизий) исправлений, логически сильно связанных с направлением основной ветви разработки, то есть для организации ветвления на 2-3 ревизии с последующим слиянием с основной ветвью. Для организации больших ветвей, логически необходимых для разделения направлений разработки следует использовать именованные ветви.

Именованное ветвление

Именованная ветвь

Рисунок 3. Именованные ветви в репозитории

Вполне естественно, что в Mercurial предусмотрен способ создания ветвей разработки с некоторыми именами, задаваемыми пользователем. Для организации подобных ветвлений предназначена команда hg branch. С помощью этой команды версия, находящаяся в локальной копии помечается ветвью с новым именем, при этом сама ветвь будет создана только после того, как вы выполните коммит. Попробуем сейчас сделать именованную ветвь, родительской ревизией для которой будет ff8f:

$ hg branch new_feature
marked working directory as branch new_feature
$ hg commit
$ hg branches
new_feature                    6:4d530267d302
default                        5:ff8ffd5270cb

Итак мы видим, что Mercurial уже знает про две именованные ветви, "вершинами" для которых являются ревизии ff8f и 4d53, хотя на графе ревизий это одна ветвь. На рисунке 3 я показал, что именно понимается под именованной ветвью в Mercurial, при этом, фактически, для каждой ветви есть своя "вершина" (tip), хотя hg log это не показывает (я приведу только смысловой отрывок):

$ hg log
changeset:   6:4d530267d302
branch:      new_feature
tag:         tip
user:        mike@mike-vbox
date:        Sun Jan 31 21:31:07 2010 +0300
summary:     Создаие именованной ветви в репозитории

changeset:   5:ff8ffd5270cb
parent:      2:66c5686e355e
user:        mike@mike-vbox
date:        Sun Jan 31 18:56:23 2010 +0300
summary:     Создание анонимной ветви

changeset:   4:6d6c634e2e20
parent:      3:6872fa960507
parent:      2:66c5686e355e
user:        mike@mike-vbox
date:        Sun Jan 10 20:34:21 2010 +0300
summary:     Выполнен мерж двух веток

changeset:   3:6872fa960507
parent:      1:270e49e72f4b
user:        mike@mike-vbox
date:        Sun Jan 10 19:40:45 2010 +0300
summary:     Файл second.txt создан во втором репозитории

Убедиться в том, что "вершины" все таки существуют можно с помощью hg update, то есть переключившись на другую ветвь:

$ hg update default
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg ident
ff8ffd5270cb

А затем переключится обратно:

$ hg update new_feature
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg ident
4d530267d302 (new_feature) tip

При этом в нашем репозитории сложилась интересная ситуация. tip ветви default не совпадает с "головой" (head) этой же ветви. В этом легко убедиться попросив Mercurial сказать какие же "головы" в нашем репозитории:

$ hg heads
changeset:   6:4d530267d302
branch:      new_feature
tag:         tip
user:        mike@mike-vbox
date:        Sun Jan 31 21:31:07 2010 +0300
summary:     Создаие именованной ветви в репозитории

changeset:   4:6d6c634e2e20
parent:      3:6872fa960507
parent:      2:66c5686e355e
user:        mike@mike-vbox
date:        Sun Jan 10 20:34:21 2010 +0300
summary:     Выполнен мерж двух веток

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

Преимущества

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

Недостатки

К недостаткам, в некоторой степени, можно отнести синдром разрастания ветвей. То есть, если вы будете использовать именованные ветви при каждой необходимости отпочковаться от основной, вывод команды hg branches будет просто гиганским через некоторое время. Хотя ветви можно закрывать при коммитах (опция --close-branch), не стоит делать именованные ветви там где они не нужны.

Ветвление в клонах репозитория

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

Преимущества

Немного более безопаснее чем при других способах. На самом деле в данном случае способ выстрелить себе в ногу только один - запушить что нибудь этакое в удаленный репозиторий, тогда как при локальном ветвлении способов выстрелить себе в ногу немного больше.

Недостатки

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

На этом я заканчиваю рассматривать ветвления в Mercurial. Думаю следующая статья будет посвящена способам слияния ветвей, а также сложным моментам при слиянии, при упоминании которых у пользователей Subversion резко падает давление и начинают трястись руки. :)

Уважаемые читатели. Просьба комментировать посты. Возможно я упустил какие-то моменты, требующие разъяснения, о которых стоило бы написать.

воскресенье, 10 января 2010 г.

Введение в Mercurial. Часть 3. Начинаем ветвиться и сливаться

Продолжаю писать про систему контроля версий 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 ;) ).

Progg it