Глава 2. Экскурсия по Mercurial: основы

Содержание

2.1. Установка Mercurial на вашем компьютере
2.1.1. Windows
2.1.2. Mac OS X
2.1.3. Linux
2.1.4. Solaris
2.2. Начало работы
2.2.1. Встроенная справка
2.3. Работа с репозиторием
2.3.1. Создание локальной копии репозитория
2.3.2. Что есть в репозитории?
2.4. Путешествие по истории
2.4.1. Изменения, ревизии и общение с другими людьми
2.4.2. Просмотр определенных ревизий
2.4.3. Подробности
2.5. Об опциях команд
2.6. Создание и анализ изменений
2.7. Запись изменений в новую ревизию
2.7.1. Установка имени пользователя
2.7.2. Описание ревизии
2.7.3. Написание хорошего сообщения к коммиту ревизии
2.7.4. Отмена публикации ревизии
2.7.5. Полюбуемся на наше творение
2.8. Распространение изменений
2.8.1. Получение (вытягивание) изменений из другого репозитория
2.8.2. Обновление рабочего каталога
2.8.3. Передача (проталкивание) изменений в другой репозиторий
2.8.4. Размещение по умолчанию
2.8.5. Распространение изменений по сети
2.9. Начало нового проекта

2.1. Установка Mercurial на вашем компьютере

Прекомпилированные пакеты Mercurial доступны для каждой популярной операционной системы. Это позволяет вам начать использование Mercurial на вашем компьютере немедленно.

2.1.1. Windows

Лучшей версией Mercurial для windows является TortoiseHg, который можно найти на сайте http://tortoisehg.org. Этот пакет не имеет внешних зависимостей, он «просто работает». Он позволяет использовать командную строку и графический пользовательский интерфейс.

2.1.2. Mac OS X

Ли Канти публикует инсталлятор mercurial для mac os x на http://mercurial.berkwood.com.

2.1.3. Linux

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

Чтобы не усложнять процесс, я сфокусируюсь на установке Mercurial из командной строки в наиболее популярных дистрибутивах Linux. Большинство из них располагают менеждерами пакетов с графическим интерфейсом, что позволит вам установить Mercurial нажатием одной кнопки. Пакет для установки называется mercurial.

  • Ubuntu и Debian:

    apt-get install mercurial
  • Fedora:

    yum install mercurial
  • OpenSUSE:

    zypper install mercurial
  • Gentoo:

    emerge mercurial

2.1.4. Solaris

SunFreeWare, на http://www.sunfreeware.com , предоставляет готовые пакеты Mercurial.

2.2. Начало работы

Для начала выполним команду hg version, чтобы удостовериться, что Mercurial установлен правильно. Какая версия на самом деле — неважно, главное, что она вообще что-то выводит.

$ hg version
Mercurial Distributed SCM (version 2.0)
(see http://mercurial.selenic.com for more information)

Copyright (C) 2005-2011 Matt Mackall and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

2.2.1. Встроенная справка

У Mercurial есть встроенная справка. Очень сложно переоценить ее наличие, особенно когда ваша работа остановилась из-за того, что вы не можете вспомнить как выполнить какую-то команду. Если Вы застопорились, просто выполните hg help, и на экран выведется краткий список команд с описанием назначения каждой из них. Если же вы еще и укажите конкретную команду (см. ниже), выведется подробная информация именно о ней.

$ hg help init
hg init [-e CMD] [--remotecmd CMD] [DEST]

create a new repository in the given directory

    Initialize a new repository in the given directory. If the given directory
    does not exist, it will be created.

    If no directory is given, the current directory is used.

    It is possible to specify an "ssh://" URL as the destination. See "hg help
    urls" for more information.

    Returns 0 on success.

options:

 -e --ssh CMD       specify ssh command to use
    --remotecmd CMD specify hg command to run on the remote side
    --insecure      do not verify server certificate (ignoring web.cacerts
                    config)

use "hg -v help init" to show more info

Для большей детализации выполните hg help -v. Опция -v - сокращение от --verbose, заставит Mercurial выводить больше информации, чем обычно.

2.3. Работа с репозиторием

В Mercurial все происходит внутри репозитория. Репозиторий проекта содержит все файлы, которые «относятся» к проекту, а также историю изменений этих файлов.

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

2.3.1. Создание локальной копии репозитория

Копирование репозитория кое-чем отличается. Хотя вы можете скопировать репозиторий как обычный каталог, лучше использовать встроенную команду Mercurial. Она называется hg clone, потому что создает идентичную копию существующего репозитория.

$ hg clone http://hg.serpentine.com/tutorial/hello
destination directory: hello
requesting all changes
adding changesets
adding manifests
adding file changes
added 5 changesets with 5 changes to 2 files
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Одно из преимуществ использования hg clone в том, что как мы видим выше, она позволяет клонировать репозитории по сети. Другим является то, что она запоминает, откуда мы его клонировали. Скоро мы убедимся, что это полезно, когда мы захотим принести новые изменения из другого репозитария.

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

$ ls -l
total 0
drwxr-x--- 3 slava slava 100 Feb  2 14:10 hello
$ ls hello
Makefile  hello.c

У файлов в нашем репозитории то же самое содержимое и история, как и в исходном.

Каждый репозиторий Mercurial полон, самодостаточнен и независим. Он содержит свою собственную копию файлов проекта и их историю. Склонированный репозиторий помнит, откуда он был склонирован, но не общается с тем репозиторием, да и ни с каким другим тоже, до тех пор пока вы ему не скажете.

Это означает, что вы можете свободно экспериментировать с вашим репозиторием. Это безопасно, потому что ваш репозиторий — «закрытая песочница», изменения в котором не повлияют ни на что, кроме него самого.

2.3.2. Что есть в репозитории?

Когда мы более пристально присмотримся к репозиторию, мы увидим, что он содержит каталог под названием .hg. Это место, где Mercurial хранит все метаданные репозитория.

$ cd hello
$ ls -a
.  ..  .hg  Makefile  hello.c

Содержание каталога .hg и его подкаталогов является собственностью Mercurial. Со всеми остальными файлами и каталогами в репозитории мы можем делать что угодно.

Строго говоря, каталог .hg — это и есть «настоящий» репозиторий, а все остальные файлы и каталоги рядом с ним называются рабочим каталогом. Разницу запомнить довольно просто — репозиторий содержит всю историю вашего проекта, в то время как рабочий каталог содержит слепок вашего проекта в определенной точке истории.

2.4. Путешествие по истории

Самое первое, что вы захотите сделать с новым, неизвестным репозиторием — изучить его историю. Команда hg log предназначена как раз для этого.

$ hg log
changeset:   4:2278160e78d4
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:16:53 2008 +0200
summary:     Trim comments.

changeset:   3:0272e0d5a517
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:08:02 2008 +0200
summary:     Get make to generate the final binary from a .o file.

changeset:   2:fef857204a0c
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:05:04 2008 +0200
summary:     Introduce a typo into hello.c.

changeset:   1:82e55d328c8c
user:        mpm@selenic.com
date:        Fri Aug 26 01:21:28 2005 -0700
summary:     Create a makefile

changeset:   0:0a04b987be5a
user:        mpm@selenic.com
date:        Fri Aug 26 01:20:50 2005 -0700
summary:     Create a standard "hello, world" program

По умолчанию, эта команда выводит краткую информацию о каждом изменении в проекте, которое было зафиксировано. В терминологии Mercurial мы называем эти зафиксированные события ревизией (changeset), потому что она может содержать изменения в различных файлах.

hg log выводит записи со следующими полями:

  • changeset (ревизия). Она состоит из десятичного числа, двоеточия и строки шестнадцатеричных цифр. Это идентификаторы ревизии. Строка шестнадцатеричных цифр представляет собой уникальный идентификатор: та же строка шестнадцатеричных цифр будет обозначать этот набор изменений в каждой копии этого хранилища. Номер короче и проще напечатать, чем строку шестнадцатеричных цифр, но он не является уникальным: это же число в двух различных копиях репозитория могут иметь различные ревизии.

  • user (пользователь). Идентификатор человека, создавшего ревизию. Там может находится все, что угодно, но чаще это имя человека и адрес электронной почты.

  • date. Дата и время, когда была создана ревизия, а также часовой пояс, в котором она была создана. (Дата и время приведены относительно этого часового пояса, они указывают сколько времени было для того, кто создал ревизию)

  • summary. Первая строка комментария к ревизии, который оставил её автор.

  • Некоторые наборы изменений, таких, как первая, имеют поле tag (тэг). Теги это еще один способ идентифицировать набор изменений, придав ему легкое для запоминания имя. (Тег с названием tip специальный: он всегда относится к новейшему изменению в репозитарии.)

По умолчанию hg log выводит очень общие сведения, с отсутствием множества деталей.

Рисунок 2.1, «Графическое представление истории репозитория hello» содержит графическое представление истории репозитория hello, что слегка облегчает понимание того, в каком направлении «развивается» его история. Мы будем возвращаться к этому рисунку в этой и следующей главах.

Рисунок 2.1. Графическое представление истории репозитория hello

XXX add text

2.4.1. Изменения, ревизии и общение с другими людьми

Английский язык печально известен своей небрежностью, а компьютерная наука имеет обширную историю неразберихи в терминах (зачем один термин, если можно использовать четыре). В контроле версий есть множество слов и фраз, означающих одно и то же. Если речь идет об истории в Mercurial, вы увидите, что слово «changeset» (набор изменений), обычно сокращается до «change», или (при письме), до «cset», а иногда «changeset» называют «revision» (ревизия) или «rev».

Не важно, какое слово вы используете для концепции «ревизии», идентификатор, по которому вы ссылаетесь на «определенную ревизию» имеет гораздо большее значение. Вспомните, что ревизия в выводе команды hg log идентифицируется номером и шестнадцатеричной строкой.

  • Номер ревизии удобен для записи, но действителен только в пределах своего репозитория.

  • Шестнадцатеричная строка — постоянный, неизменный параметр, который всегда идентифицирует одну и ту же ревизию в каждой копии репозитория.

Это различие очень важно. Если вы отправите кому-нибудь письмо с упоминанием «ревизии 33», существует большая вероятность, что их ревизия 33 будет совсем другой. Причиной этого является то, что номер ревизии зависит от порядка, в котором ревизии попадают в репозиторий, и нет никакой гарантии, что одни и те же изменения произойдут в одинаковом порядке в различных репозиториях. Три изменения А, Б и В запросто могут появится в одном репозитории в порядке 0,1,2, а в другом — 0,2,1.

Mercurial использует номера ревизий исключительно для удобства. Если вам нужно обсудить с кем-то конкретную ревизию, или по какой-то другой причине ссылаться на нее (в багтрекере, например), используйте шестнадцатеричный идентификатор.

2.4.2. Просмотр определенных ревизий

Чтобы ограничить вывод команды hg log до одной ревизии, используйте опцию -r (или --rev). Вы можете использовать или номер ревизии или ее идентификатор, а также запросить ревизий столько, сколько вам захочется.

$ hg log -r 3
changeset:   3:0272e0d5a517
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:08:02 2008 +0200
summary:     Get make to generate the final binary from a .o file.

$ hg log -r 0272e0d5a517
changeset:   3:0272e0d5a517
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:08:02 2008 +0200
summary:     Get make to generate the final binary from a .o file.

$ hg log -r 1 -r 4
changeset:   1:82e55d328c8c
user:        mpm@selenic.com
date:        Fri Aug 26 01:21:28 2005 -0700
summary:     Create a makefile

changeset:   4:2278160e78d4
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:16:53 2008 +0200
summary:     Trim comments.

Если вы хотите увидеть историю нескольких ревизий, но не хотите просматривать их все, можете указать диапазон, как бы выражая мысль: «Мне нужны все ревизии от А до Б включительно».

$ hg log -r 2:4
changeset:   2:fef857204a0c
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:05:04 2008 +0200
summary:     Introduce a typo into hello.c.

changeset:   3:0272e0d5a517
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:08:02 2008 +0200
summary:     Get make to generate the final binary from a .o file.

changeset:   4:2278160e78d4
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:16:53 2008 +0200
summary:     Trim comments.

Mercurial учитывает порядок, в котором ревизии были указаны, так что hg log -r 2:4 выводит ревизии 2,3 и 4. Тогда как hg log -r 4:2 — 4,3 и 2.

2.4.3. Подробности

В то время как информация, которую выводит hg log полезна, если вы знаете что ищете, вам может понадобиться полное описание изменений или список измененных файлов, если вы хотите узнать та ли это ревизия, что вам нужна. Команда hg log с аргументом -v (--verbose) предоставит вам такую возможность.

$ hg log -v -r 3
changeset:   3:0272e0d5a517
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:08:02 2008 +0200
files:       Makefile
description:
Get make to generate the final binary from a .o file.


Если вы хотите видеть описание и то как изменялось содержимое, добавьте опцию -p (или --patch). Будет показываться содержание изменений в едином diff (если вы никогда не видели формат унифицированного diff раньше, см. Раздел 12.4, «Понимание патчей»).

$ hg log -v -p -r 2
changeset:   2:fef857204a0c
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:05:04 2008 +0200
files:       hello.c
description:
Introduce a typo into hello.c.


diff -r 82e55d328c8c -r fef857204a0c hello.c
--- a/hello.c	Fri Aug 26 01:21:28 2005 -0700
+++ b/hello.c	Sat Aug 16 22:05:04 2008 +0200
@@ -11,6 +11,6 @@
 
 int main(int argc, char **argv)
 {
-	printf("hello, world!\n");
+	printf("hello, world!\");
 	return 0;
 }

Опция -p очень полезна, поэтому следует её запомнить.

2.5. Об опциях команд

Давайте сделаем перерыв в изучении команд Mercurial и обсудим шаблоны работы с ними. Это будет вам полезно, когда мы продолжим наше турне.

Mercurial имеет простой и последовательный подход в работе с опциями, которые вы можете передавать командам. Это следует из соглашений по опциям, которые являются общими для современных Linux и Unix систем.

  • Каждая опция имеет длинное имя. К примеру, как мы уже видели, команда hg log принимает параметр --rev.

  • Также большинство опций имеют короткие имена. Вместо --rev можно использовать -r. Не все опции имеют короткие имена, потому как некоторые из них просто редко используются.

  • Длинные опции начинаются с двух тире (--rev), а короткие начинаются с одного (-r).

  • Имена опций и их применение в командах согласовано. Например, все команды, позволяющие указывать ID или номер ревизии принимают оба аргумента -r и --rev.

  • При использовании коротких опций, вы можете печатать их вместе. Например, команда hg log -v -p -r 2 может быть записана в виде hg log -vpr2.

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

Большинство выводящих какую-то информацию команд выдадут большее количество информации, если им передать опцию -v (или --verbose), или меньшее, если передать опцию -q (или --quiet).

[Примечание] Вариант наименования опций

Почти всегда в командах Mercurial названия опций придерживаются единой концепции. Для примера, если команда работает с наборами изменений, вы всегда будете идентифицировать их опциями --rev или -r. Такая последовательность в использовании имён делает более лёгким запоминание опций различных команд.

2.6. Создание и анализ изменений

Так как мы уже умеем просматривать историю в Mercurial, можно заняться внесением изменений и их изучением.

Первое, что необходимо сделать — изолировать наш эксперимент в его собственном репозитории. Для этого используется команда hg clone, но так как у нас уже есть копия репозитория локально, то мы можем клонировать её вместо клонирования по сети. Это действие значительно быстрее, а кроме того, в большинстве случаев использует меньше места на диске[1].

$ cd ..
$ hg clone hello my-hello
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd my-hello

Кстати говоря, хорошей идеей является хранение «нетронутой» копии удалённого репозитория, который можно использовать для создания временных клонов для получения «песочниц» для каждой задачи, над которой вам хочется поработать. Это позволяет вам работать над множеством задач одновременно, изолированных друг от друга до завершения и вашей готовности их интегрировать. Потому как локальные клоны настолько «дёшевы», что их создание и уничтожение в любое удобное время практически не несёт накладных расходов.

В нашем хранилище my-hello, мы имеем файл hello.c — классическую программу «Hello, World».

$ cat hello.c
/*
 * Placed in the public domain by Bryan O'Sullivan.  This program is
 * not covered by patents in the United States or other countries.
 */

#include <stdio.h>

int main(int argc, char **argv)
{
	printf("hello, world!\");
	return 0;
}

Давайте отредактируем этот файл чтоб он печатал на выходе вторую строку.

# ... edit edit edit ...
$ cat hello.c
/*
 * Placed in the public domain by Bryan O'Sullivan.  This program is
 * not covered by patents in the United States or other countries.
 */

#include <stdio.h>

int main(int argc, char **argv)
{
	printf("hello, world!\");
	printf("hello again!\n");
	return 0;
}

Команда Mercurial hg status покажет, что Mercurial знает о файлах в репозитории.

$ ls
Makefile  hello.c
$ hg status
M hello.c

hg status выводит информацию не обо всех файлах, а только об изменённых: это строка, начинающаяся с буквы «M» для hello.c. Пока вы не укажете это специально, hg status не будет выводить информацию про файлы, которые не изменились.

«M» показывает, что Mercurial оповещен о модификации hello.c. Мы не уведомляли Mercurial о том, что изменили файл ни перед, ни после окончания работы, он способен самостоятельно находить изменения.

Иногда мало просто знать только о факте изменения файла, и мы предпочли бы знать, какие точно изменения были сделаны. Для этого используется команда hg diff.

$ hg diff
diff -r 2278160e78d4 hello.c
--- a/hello.c	Sat Aug 16 22:16:53 2008 +0200
+++ b/hello.c	Thu Feb 02 14:10:17 2012 +0000
@@ -8,5 +8,6 @@
 int main(int argc, char **argv)
 {
 	printf("hello, world!\");
+	printf("hello again!\n");
 	return 0;
 }
[Подсказка] Объяснение патчей

Напомню, что нужно посмотреть Раздел 12.4, «Понимание патчей», если вы не знаете, как читать вывод команды выше.

2.7. Запись изменений в новую ревизию

Мы можем изменять файлы, собирать и тестировать изменения и использовать команды hg status, hg diff для анализа изменений, пока мы не будем удовлетворены ими и не достигнем естественной точки, когда захочется записать проделанную работу в новую ревизию.

Команда hg commit позволяет создать новую ревизию. Для простоты, мы будем называть этот процесс «сделать коммит» или «закоммитить».

2.7.1. Установка имени пользователя

В первый раз выполнение команды hg commit может пройти неудачно. Mercurial записывает ваше имя и адрес в каждую ревизию, чтобы вы или другие пользователи могли связаться с автором каждого изменения. Mercurial пытается найти наиболее разумное имя пользователя для коммита. Поиск происходит в следующем порядке:

  1. Если в команде hg commit вы указали опцию -u, с последующим именем пользователя, то оно будет обладать наивысшим приоритетом.

  2. Если у вас установлена переменная окружения HGUSER, то следующей будет проверена она.

  3. Если вы создали в своей домашней директории файл .hgrc, и в нём есть директива username — будет использована она. Чтобы узнать, как должен выглядеть содержимое этого файла, смотрите Раздел 2.7.1.1, «Создание файла конфигурации Mercurial».

  4. Если у вас установлена переменная окружения EMAIL, то будет использована она.

  5. Mercurial использует локальное имя пользователя и хоста в системе, чтобы создать конечное имя. Так как часто полученное имя малополезно, то будет выведено предупреждение.

Если все варианты поиска завершились неудачно, Mercurial выведет сообщение об ошибке и не позволит создать коммит, пока вы не укажете имя пользователя.

Переменная HGUSER и опция -u в команде hg commit должны использоватся для изменения стандартного способа выбора имени. Для нормальной эксплуатации наиболее простой и надежный путь: установить имя в файле .hgrc. Подробности смотри ниже.

2.7.1.1. Создание файла конфигурации Mercurial

Чтобы установить имя пользователя откройте свой любимый текстовый редактор и создайте файла .hgrc в домашней директории. Mercurial будет использовать этот файл для поиска персональных настроек. Первоначальное содержание этого файла должно выглядить примерно так.

[Подсказка] «Домашняя директория» в Windows

Домашний каталог, на английской установке Windows это, как правило, папка C:\Documents and Settings\<имя пользователя>. Вы можете узнать точное название вашей домашней директории, открыв окно командной строки и выполнив следующую команду.

C:\> echo %UserProfile%
# This is a Mercurial configuration file.
[ui]
username = Firstname Lastname <email.address@example.net>

Строка «[ui]» обьявляет секцию конфигурационного файла. Вы можете прочитать «username = ...» как «установить значение переменной username в секции ui». Секции продолжаются до начала новых секций. Пустые строки и строки, начинаюшиеся с «#» игнорируются.

2.7.1.2. Выбор имени пользователя

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

[Примечание] Примечание

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

2.7.2. Описание ревизии

Когда мы фиксируем изменения, Mercurial переводит нас в текстовый редактор, чтобы ввести комментарий, описывающее модификации, которые мы внесли в этом наборе изменений. Такое описание называется сообщением об изменениях (описанием изменений, описанием ревизии). Это будет записью для читателей о том, что мы сделали и почему, и будет выводиться при выполнении команды hg log после того, как мы закончим публикацию ревизии.

$ hg commit

Редактор, который откроется при выполнении команды hg commit, будет содержать пустую строку и несколько строк, начинающихся с «HG:».

This is where I type my commit comment.

HG: Enter commit message.  Lines beginning with 'HG:' are removed.
HG: --
HG: user: Bryan O'Sullivan <bos@serpentine.com>
HG: branch 'default'
HG: changed hello.c

Mercurial игнорирует строки, начинающиеся с «HG:». Он использует их только для того, чтобы сообщить нам, изменения в каких файлах он запишет. Редактирование или удаление этих строк ни на что не повлияет.

2.7.3. Написание хорошего сообщения к коммиту ревизии

Команда hg log по умолчанию выводит только первую строку описания изменений. Поэтому комментарий лучше написать так, чтобы первая строка была отделена. Вот хороший пример плохого комментария:

changeset:   73:584af0e231be
user:        Censored Person <censored.person@example.org>
date:        Tue Sep 26 21:37:07 2006 -0700
summary:     include buildmeister/commondefs. Add exports.

Для оставшейся части описания ревизии нет жестких правил. Сам Mercurial не обрабатывает и не заботится о содержимом сообщения об изменениях, хотя в вашем проекте могут быть правила, предписывающие определённое форматирование.

Моё личное предпочтение — короткие, но содержательные комментарии, которые сообщают мне то, чего я не могу выяснить при беглом взгляде на вывод команды hg log --patch.

Если мы выполним команду hg commit без каких-либо аргументов, запишутся все изменения, которые мы сделали, как сообщил hg status и hg diff.

[Примечание] Сюрприз для пользователей Subversion

Если мы явно не укажем имена файлов для ревизии, hg commit как и любая другая команда Mercurial будет действовать для всего рабочего каталога репозитория. Учтите это, если вы переходите из мира Subversion или CVS, где подобная команда работает только для текущего каталога и его подкаталогов.

2.7.4. Отмена публикации ревизии

Если вы передумаете публиковать изменения во время редактирования комментария, просто выйдите из редактора без сохранения изменяемого файла. Это не вызовет изменений ни в репозитории, ни в рабочем каталоге.

2.7.5. Полюбуемся на наше творение

Закончив публикацию ревизии, мы можем воспользоваться командой hg tip для показа только что созданного набора изменений. Вывод этой команды похож на вывод команды hg log, но отображает только последнюю версию в репозитории.

$ hg tip -vp
changeset:   5:c3e3be994861
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:10:17 2012 +0000
files:       hello.c
description:
Added an extra line of output


diff -r 2278160e78d4 -r c3e3be994861 hello.c
--- a/hello.c	Sat Aug 16 22:16:53 2008 +0200
+++ b/hello.c	Thu Feb 02 14:10:17 2012 +0000
@@ -8,5 +8,6 @@
 int main(int argc, char **argv)
 {
 	printf("hello, world!\");
+	printf("hello again!\n");
 	return 0;
 }

Мы называем последнюю ревизию конечной (или верхней) ревизией или просто главной.

Кстати, команда hg tip принимает многие из опций команды hg log. Так, например, -v означает «более подробно», -p«показать патч». Использование -p для показа патча является еще одним примером последовательного подхода к именованию опций.

2.8. Распространение изменений

Ранее мы упоминали, что репозитории в Mercurial самодостаточны. Это значит, что только что созданный набор изменений существует лишь в нашем репозитории my-hello. Давайте рассмотрим несколько способов, которыми мы можем распространить это изменение в другие репозитории.

2.8.1. Получение (вытягивание) изменений из другого репозитория

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

$ cd ..
$ hg clone hello hello-pull
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Мы будем использовать команду hg pull, для получения изменений из my-hello в hello-pull. Однако вытягивание вслепую неизвестных изменений в репозиторий может быть пугающей перспективой. Mercurial позволяет узнать с помощью команды hg incoming, какие изменения команда hg pull вытянет в репозиторий без реального применения изменений.

$ cd hello-pull
$ hg incoming ../my-hello
comparing with ../my-hello
searching for changes
changeset:   5:c3e3be994861
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:10:17 2012 +0000
summary:     Added an extra line of output

Получение изменений в репозиторий означает выполнение команды hg pull и указание ей, из какого репозитория следует вытягивать.

$ hg tip
changeset:   4:2278160e78d4
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:16:53 2008 +0200
summary:     Trim comments.

$ hg pull ../my-hello
pulling from ../my-hello
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
(run 'hg update' to get a working copy)
$ hg tip
changeset:   5:c3e3be994861
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:10:17 2012 +0000
summary:     Added an extra line of output

Как видно из вывода команды hg tip до и после, мы успешно вытянули изменения в наш репозиторий. Тем не менее, вытягивание изменений в Mercurial отделено от обновления рабочей директории. Остается один шаг до того, как мы увидим изменения в рабочем каталоге.

[Подсказка] Вытягивание конкретных изменений

Вполне возможно, что из-за задержки между выполнением команд hg incoming и hg pull, вы можете увидеть не все ревизии, которые будут добавлены из другого репозитория. Предположим, вы тянете изменения из репозитория где-то в сети. Пока вы смотрите вывод hg incoming, перед тем как вытащить изменения, кто-то может что-то совершить в удаленном репозитории. То есть можно вытянуть больше изменений, чем вы видели при использовании hg incoming.

Если вы хотите получить именно те изменения, которые были указаны в hg incoming, или вам требуется некоторое подмножество изменений, просто укажите id ревизии, которую хотите получить, например hg pull -r7e95bb.

2.8.2. Обновление рабочего каталога

До сих пор мы говорили о связи между репозиторием и его рабочим каталогом. Команда hg pull, которую мы выполнили в Раздел 2.8.1, «Получение (вытягивание) изменений из другого репозитория», вытянула изменения в наш репозиторий. Но если проверить, в рабочем каталоге нет ни следа этих изменений. Это потому, что hg pull не трогает (по умолчанию) рабочий каталог. Для применения изменений мы используем команду hg update.

$ grep printf hello.c
	printf("hello, world!\");
$ hg update tip
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ grep printf hello.c
	printf("hello, world!\");
	printf("hello again!\n");

Может показаться странным то, что hg pull не обновляет рабочий каталог автоматически. На самом деле этому есть веская причина: вы можете использовать hg update, для обновления рабочего каталога до состояния, в котором он был в любой ревизии в истории репозитория. Если бы вы обновили рабочий каталог до старой версии — например, чтобы отыскать причину ошибки, — а затем выполнили бы hg pull, которая обновляет рабочий каталог автоматически до новой версии, вы были бы ужасно недовольны этим.

Однако, так как вытягивание с последующим обновлением является распространенным явлением, Mercurial позволяет вам совместить их передачей команде hg pull ключа -u.

Если вы снова посмотрите на вывод команды hg pull в Раздел 2.8.1, «Получение (вытягивание) изменений из другого репозитория», когда мы выполнили её без -u, вы увидите, что в конце вывода было полезное напоминание о необходимости явного действия для обновления рабочего каталога.

Чтобы узнать, какая ревизия у рабочего каталога, используйте команду hg parents.

$ hg parents
changeset:   5:c3e3be994861
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:10:17 2012 +0000
summary:     Added an extra line of output

Если вы снова взглянете на Рисунок 2.1, «Графическое представление истории репозитория hello», вы увидите стрелки, соединяющие между собой каждую последующую ревизию. Вершина, из которой в каждом случае ведёт стрелка, — родитель, а та вершина, куда стрелка ведёт, — потомок. Аналогично, у рабочего каталога есть родитель — это набор изменений, который содержится в данный момент в рабочем каталоге.

Чтобы обновить рабочий каталог до конкретной ревизии, передайте номер или идентификатор ревизии команде hg update.

$ hg update 2
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg parents
changeset:   2:fef857204a0c
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:05:04 2008 +0200
summary:     Introduce a typo into hello.c.

$ hg update
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg parents
changeset:   5:c3e3be994861
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:10:17 2012 +0000
summary:     Added an extra line of output

Если вы опустите явное указание ревизии, hg update обновит до верхней ревизии, как видно в примере выше при втором вызове hg update.

2.8.3. Передача (проталкивание) изменений в другой репозиторий

Mercurial позволяет передать изменения в другой репозиторий из репозитория, в котором мы в данный момент находимся. Как с примером hg pull выше, создадим временный репозиторий для передачи в него наших изменений.

$ cd ..
$ hg clone hello hello-push
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

Команда hg outgoing сообщает нам об изменениях, которые будут переданы в другой репозиторий.

$ cd my-hello
$ hg outgoing ../hello-push
comparing with ../hello-push
searching for changes
changeset:   5:c3e3be994861
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:10:17 2012 +0000
summary:     Added an extra line of output

И команда hg push вызывает настоящую передачу.

$ hg push ../hello-push
pushing to ../hello-push
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files

Как и hg pull, команда hg push не обновляет рабочую директорию репозитория, в который передаются изменения. (В отличие от hg pull, у hg push нет ключа -u, который обновлял бы рабочую директорию другого репозитория). Эта асимметрия является преднамеренной: репозиторий в который мы передаём изменения может находиться на удаленном сервере и распределяться между несколькими людьми. Если бы нам пришлось обновить свой рабочий каталог в то время как кто-то работал в нём, его работа была бы нарушена.

Что же произдойдёт, если мы попробуем получить или передать изменения, а в принимающем репозитории они уже есть? Ничего особенного.

$ hg push ../hello-push
pushing to ../hello-push
searching for changes
no changes found

2.8.4. Размещение по умолчанию

Когда мы клонируем репозиторий, Mercurial записывает расположение репозитория из которого мы делали клон в файле .hg/hgrc нового репозитория. Если мы не будем указывать место для hg pull (откуда) или для hg push (куда), эти команды будут использовать это место в качестве репозитория по умолчанию. Команды hg incoming и hg outgoing делают то же самое.

Если вы откроете файл .hg/hgrc в текстовом редакторе, вы увидите содержимое похожее на следующее.

[paths]
default = http://www.selenic.com/repo/hg

Возможно — и часто полезно — чтобы путь по умолчанию для hg push и hg outgoing, отличался от пути для hg pull и hg incoming. Мы можем так сделать, добавив запись default-push в секцию [paths] файла .hg/hgrc.

[paths]
default = http://www.selenic.com/repo/hg
default-push = http://hg.example.com/hg

2.8.5. Распространение изменений по сети

Команды, которые мы затронули в нескольких предыдущих разделах, не ограничиваются работой с локальными репозиториями. Любая из них работает таким же способом и через сеть — просто укажите команде URL вместо локального пути.

$ hg outgoing http://hg.serpentine.com/tutorial/hello
comparing with http://hg.serpentine.com/tutorial/hello
searching for changes
changeset:   5:c3e3be994861
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Thu Feb 02 14:10:17 2012 +0000
summary:     Added an extra line of output

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

$ hg push http://hg.serpentine.com/tutorial/hello
pushing to http://hg.serpentine.com/tutorial/hello
searching for changes
remote: ssl required

2.9. Начало нового проекта

Начать новый проект так же просто, как и использовать уже существующий. Команда hg init создает новый, пустой репозиторий Mercurial.

$ hg init myproject

Это просто создаст репозиторий с именем myproject в текущем каталоге.

$ ls -l
total 8
-rw-r----- 1 slava slava 47 Feb  2 14:09 goodbye.c
-rw-r----- 1 slava slava 45 Feb  2 14:09 hello.c
drwxr-x--- 3 slava slava 60 Feb  2 14:09 myproject

Можно сказать, что myproject это репозиторий Mercurial, потому что он содержит каталог .hg.

$ ls -al myproject
total 0
drwxr-x--- 3 slava slava  60 Feb  2 14:09 .
drwx------ 3 slava slava 140 Feb  2 14:09 ..
drwxr-x--- 3 slava slava 100 Feb  2 14:09 .hg

Если мы хотим добавить существующие файлы в репозиторий, мы копируем их внутрь рабочей директории, и с помощью команды hg add сообщаем Mercurial, что нужно начинать за ними следить.

$ cd myproject
$ cp ../hello.c .
$ cp ../goodbye.c .
$ hg add
adding goodbye.c
adding hello.c
$ hg status
A goodbye.c
A hello.c

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

$ hg commit -m 'Initial commit'

Старт нового проекта с Mercurial займёт всего несколько мгновений, и это является частью его привлекательности. Контроль версий стал сейчас настолько простым, что мы можем его использовать даже в маленьких проектах, в которых, возможно, не использовали бы более сложные инструменты.



[1] Экономия пространства возникает, когда источник и получатель репозитория находятся на одной файловой системе, в этом случае Mercurial будет использовать жесткие ссылки, чтобы использовать политику совместного использования копрование-при-записи для его внутренних метаданных. Если это объяснение ничего для вас не значит, не беспокойтесь: все происходит прозрачно и автоматически, и вам не нужно ничего понимать.