«Одна голова — хорошо, а две лучше». Эта пословица как нельзя лучше походит к теме сегодняшней статьи о Меркуриале. После изучения в предыдущей статье базовых приемов и команд, необходимых для начала работы с Меркуриалом, мы перейдем к более углубленному изучению, и рассмотрим сегодня некоторые детали совместной работы, с использованием системы контроля версий.
Во второй главе, мы научились клонировать репозитории, делать изменения, а также заливать произведенные изменения из одного репозитория в другой. Теперь настало время для следующего шага – слияния изменений из различных репозиториев.
Слияние потоков работы
Слияние это фундаментальная часть работы с инструментом управления распределенными ревизиями. Существуют различные ситуации, в которых нам может понадобиться объединить нашу работу, например:
- Элис и Боб работают над одним проектом, причем каждый из них имеет свою собственную копию репозитория. Элис исправляет баг в своем репозитории, Боб добавляет новую функцию, в своем. Они хотят открыть доступ к репозиторию, содержащему и исправленную ошибку, и новую функцию.
- Синтия часто работает над несколькими, различными задачами для одного проекта, каждая из задач безопасно изолирована от основного репозитория. Работа такого рода, предполагает частую необходимость слияния небольших изменений с основным репозиторием.
Поскольку нам часто приходится объединять работу, Меркуриал упростил этот процесс. Давайте попробуем слить два репозитория. Начнем с создания копии существующего репозитория (помните, как это делается?) и произведения в нем небольших изменений.
$ cd ..
$ hg clone hello my-new-hello
updating working directory
2 files updated, 0 files merged, 0 files removed, 0 files unresolved$ cd my-new-hello
# Делаем небольшие изменения в файле hello.c.
$ my-text-editor hello.c
$ hg commit -m 'A new hello for a new day.'
Теперь у нас есть две копии файла hello.c, с различным содержанием. Истории двух репозиториев также изменяются, как показано на рисунке 3.1. Вот копия нашего файла из первого репозитория.
$ 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.
*/#includeint main(int argc, char **argv)
{
printf("once more, hello.n");
printf("hello, world!");
printf("hello again!n");
return 0;
}
И вот наша, немного измененная версия из другого репозитория.
$ cat ../my-hello/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.
*/#includeint main(int argc, char **argv)
{
printf("hello, world!");
printf("hello again!n");
return 0;
}
Рисунок 3.1 Расхождение историй у репозитория my-hello и my-new-hello
Мы уже знаем, что затягивание изменений (hg pull) из репозитория my-hello, не произведет изменений в рабочем каталоге.
$ 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 (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
Однако, команда hg pull кое-что говорит «заголовкам».
Головные наборы изменений
Помните, что Меркуриал записывает родителя, у каждого изменения. Если конкретное изменение имеет родителя, мы называем его потомком родителя. Головные изменения, или попросту голова – это набор изменений, не имеющий потомков. Самая верхняя ревизия, таким образом, является головой, поскольку новейшая ревизия в репозитории не имеет дочерних ревизий. Бывают и такие случаи, когда репозиторий может содержать несколько голов.
Рисунок 3.2 Содержимое репозитория my-new-hello после затягивания изменений из репозитория my-hello
На картинке 3.2, вы можете увидеть данный эффект, полученный в результате затягивания изменений из репозитория my-hello, в репозиторий my-new-hello. История изменений, присутствующая в my-new-hello, осталась нетронутой, при этом добавилась наша новая ревизия. Если еще раз взглянуть на рисунок 3.1, можно заметить, что ID набора изменений в репозитории my-hello, был без изменений перенесен в репозиторий my-new-hello, изменился только номер ревизии. (Это, между прочим, отличный пример того, что небезопасно использовать номер ревизии, при обсуждении наборов изменений.)
Теперь мы можем увидеть несколько голов репозитория, используя команду hg heads.
$ hg heads
changeset: 6:b6fed4f21233
tag: tip
parent: 4:2278160e78d4
user: Bryan O'Sullivan
date: Tue May 05 06:55:53 2009 +0000
summary: Added an extra line of outputchangeset: 5:5218ee8aecf3
user: Bryan O'Sullivan
date: Tue May 05 06:55:55 2009 +0000
summary: A new hello for a new day.
Выполнение слияния
Итак, что же случится, если мы попытаемся выполнить обычную команду hg update, чтобы обновится до новейшей ревизии?
$ hg update
abort: crosses branches (use 'hg merge' or 'hg update -C')
Меркуриал скажет нам, что команда hg update не может производить слияние, он не произведет обновления рабочей директории (если мы, конечно, не заставим его), поскольку считает, что мы сначала захотим произвести слияние двух голов. (Кстати, заставляя обновлять репозиторий командой hg update –C, вы рискуете потерять любые, незакоммиченные изменения в рабочей директории).
Для того чтобы начать объединение между двумя головами, нам понадобится команда hg merge.
$ hg merge
merging hello.c
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
Мы объединили ревизии, без проблем, и содержимое файла hello.c, теперь обновлено. Это обновило рабочую директорию, так что теперь она содержит изменения из обоих голов, который можно увидеть и в содержимом файла hello.c, и в выводе команды hg parents.
$ hg parents
changeset: 5:5218ee8aecf3
user: Bryan O'Sullivan
date: Tue May 05 06:55:55 2009 +0000
summary: A new hello for a new day.changeset: 6:b6fed4f21233
tag: tip
parent: 4:2278160e78d4
user: Bryan O'Sullivan
date: Tue May 05 06:55:53 2009 +0000
summary: Added an extra line of output$ 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.
*/#includeint main(int argc, char **argv)
{
printf("once more, hello.n");
printf("hello, world!");
printf("hello again!n");
return 0;
}
Запись результата слияния
Теперь, когда слияние завершено, команда hg parents будет отображать два родителя до тех пор, пока мы не сохраним результат нашего слияния, с помощью команды hg commit.
$ hg commit -m 'Merged changes'
Теперь у нас есть новая головная ревизия, обратите внимание на то, что она содержит в себе обе, прежние головы, в качестве своих родителей. Это те же ревизии, которые мы отображали ранее, с командой hg parents.
$ hg tip
changeset: 7:ecb0e17b2a4e
tag: tip
parent: 5:5218ee8aecf3
parent: 6:b6fed4f21233
user: Bryan O'Sullivan
date: Tue May 05 06:55:56 2009 +0000
summary: Merged changes
На рисунке 3.3, вы можете увидеть изображение того, что случилось с рабочей директорией во время слияния, и как повлияла на репозиторий, запись полученных изменений. Во время слияния, рабочая директория имела два родительских набора изменений, и они стали родителями нового набора изменений.
Рисунок 3.3 Рабочая директория и репозиторий во время слияния и последующего коммита
Иногда мы говорим о слияния, имеющим стороны: левая сторона – это первая родительская ревизия, в выводе команды hg parents, а правая сторона – вторая. Если бы рабочая директория имела только 5 ревизию, то во время слияния, она была бы слева.
Как видите, объединять несколько потоков работы в один, достаточно простая задача, если вы используете Меркуриал. Но не обольщайтесь раньше времени. Сегодня в статье, мы рассмотрели очень простой пример, с небольшими изменениями, да и то в одном файле. В следующей статье, мы рассмотрим более сложный пример слияния нескольких ревизий, с разрешением конфликтных изменений.
Перевод 2 главы книги «Mercurial: The Definitive Guide», автор Bryan O’Sullivan
Отличный перевод, буду ждать продолжения!