Алексей Махоткин

домашняя страница

CVS — Система Управления Параллельными Версиями

Go to the first, previous, next, last section, table of contents.


Несколько разработчиков

Когда над программным проектом работает более одного человека, ситуация резко усложняется. Зачастую два разработчика одновременно пытаются редактировать один и тот же файл. Одно из решений, известное как блокировка файлов или блокированное извлечение, – в каждый момент времени позволять редактировать файл только одному человеку. Это – единственное решение при использовании некоторых систем контроля версий, включая RCS и SCCS. В настоящее время обычным способом совершить блокированное извлечение рабочей копии с помощью CVS – использовать команду cvs admin -l (see section Ключи команды admin). Эта возможность не столь красиво интегрирована в CVS, как функции слежения, описанные ниже, но кажется, что всех, кому требуется блокированное извлечение, эта команда устраивает.

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

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

CVS также поддерживает механизмы различных способов коммуникации, никак не настаивая на выполнении каких-либо правил, в отличие от блокированных извлечений.

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

Статус файла

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

Up-to-date
Файл идентичен последней ревизии в репозитории, находящейся на используемой ветке.
Locally Modified
Вы редактировали этот файл и еще не зафиксировали изменения.
Locally Added
Вы добавили этот файл с помощью cvs add, и еще не зафиксировали изменения.
Locally Removed
Вы удалили файл с помощью cvs remove и еще не зафиксировали изменения.
Needs Checkout
Кто-то еще поместил новую ревизию в репозиторий. Название немного сбивает с толку, потому что требуется использовать команду cvs update, а не cvs checkout, чтобы получить свежайшую версию.
Needs Patch
Похоже на “Needs Checkout”, но CVS-сервер пошлет заплату, а не целый файл. В принципе это приведет к тому же самому результату.
Needs Merge
Кто-то еще поместил новую ревизию в репозиторий, а вы также изменили этот файл.
File had conflicts on merge
Похоже на “Locally Modified”, только последняя выполненная команда cvs update обнаружила конфликт. Если вы еще не исправили его, сделайте это, как описано в section Пример конфликта.
Unknown
CVS ничего не знает об этом файле. Например, вы создали новый файл и еще не выполнили cvs add.

Чтобы уточнить состояние файла, cvs status также сообщает о Working revision, являющейся ревизией, на основе которой создан файл в рабочем каталоге, и Repository revision, являющейся свежайшей ревизией в репозитории, находящейся на используемой ветке.

Ключи команды status перечислены в section Краткий справочник по командам CVS. Информация о Sticky tag и Sticky date находится в section Липкие метки. Информация о Sticky options находится в описании флага `-k’ в section Ключи команды update.

Команды status и update можно рассматривать как соответствующие друг другу. update используется для извлечения самых свежих файлов, а status – для выяснения, что же произойдет, если выполнить update (конечно, состояние репозитория может измениться до того, как вы выполните update). В действительность, если вы хотите узнать состояние файлов в более краткой форме, выполните

$ cvs -n -q update

Ключ командной строки `-n’ указывает не выполнять обновление, а просто сообщить о состоянии файлов; `-q’ не печатает имена каждого каталога. Прочую информацию о команде update можно найти в section Краткий справочник по командам CVS.

Извлечение свежей ревизии файла

Если вы хотите получить новую ревизию файла или объединить его с другой ревизией, используйте команду update. Если вы имеете старую ревизию файла, то эта команда эквивалентна checkout: свежая ревизия извлекается из репозитория и помещается в рабочий каталог.

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

Например, представим себе, что вы извлекли ревизию 1.4 и начали редактировать ее. В это время кто-то еще поместил в репозиторий ревизию 1.5 и, вскорости, ревизию 1.6. Если теперь вы выполните команду update, CVS внедрит изменения между ревизиями 1.4 и 1.6 в ваш файл.

Если изменения между ревизиями 1.4 и 1.6 случились слишком близко к вашим изменениям, происходит пересечение. В таких случаях на экран выдается предупреждение, а в результирующем файле оказываются обе версии пересекающихся строк, разделенные специальными маркерами. See section Команда update: обновить рабочий каталог из репозитория, где полностью описана команда update.

Пример конфликта

Предположим, что ревизия 1.4 файла `driver.c’ содержит такой код:

#include <stdio.h>

void main()
{
    parse();
    if (nerr == 0)
        gencode();
    else
        fprintf(stderr, “No code generated.\n”);
    exit(nerr == 0 ? 0 : 1);
}

Ревизия 1.6 файла `driver.c’ содержит такой код:

#include <stdio.h>

int main(int argc,
         char **argv)
{
    parse();
    if (argc != 1)
    {
        fprintf(stderr, “tc: No args expected.\n”);
        exit(1);
    }
    if (nerr == 0)
        gencode();
    else
        fprintf(stderr, “No code generated.\n”);
    exit(!!nerr);
}

Ваша рабочая копия файла `driver.c’, основанная на ревизии 1.4, перед выполнением `cvs update’ содержит такой код:

#include <stdlib.h>
#include <stdio.h>

void main()
{
    init_scanner();
    parse();
    if (nerr == 0)
        gencode();
    else
        fprintf(stderr, “No code generated.\n”);
    exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

Вы выполняете `cvs update’:

$ cvs update driver.c
RCS file: /usr/local/cvsroot/yoyodyne/tc/driver.c,v
retrieving revision 1.4
retrieving revision 1.6
Merging differences between 1.4 and 1.6 into driver.c
rcsmerge warning: overlaps during merge
cvs update: conflicts found in driver.c
C driver.c

CVS сообщает, что вы встретились с конфликтами. Ваш исходный рабочий файл сохранен в `.#driver.c.1.4’. Новая версия `driver.c’ содержит такой код:

#include <stdlib.h>
#include <stdio.h>

int main(int argc,
         char **argv)
{
    init_scanner();
    parse();
    if (argc != 1)
    {
        fprintf(stderr, “tc: No args expected.\n”);
        exit(1);
    }
    if (nerr == 0)
        gencode();
    else
        fprintf(stderr, “No code generated.\n”);
<<<<<<< driver.c
    exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
=======
    exit(!!nerr);
>>>>>>> 1.6
}

Заметьте, что непересекающиеся модификации включены в вашу рабочую копию, а пересекающаяся секция четко обозначена строками `<<<<<<<’, `=======’ and `>>>>>>>’.

Разрешить конфликт можно, отредактировав файл, удалив маркеры и неверный вариант. Предположим, в результате у вас получился такой файл:

#include <stdlib.h>
#include <stdio.h>

int main(int argc,
         char **argv)
{
    init_scanner();
    parse();
    if (argc != 1)
    {
        fprintf(stderr, “tc: No args expected.\n”);
        exit(1);
    }
    if (nerr == 0)
        gencode();
    else
        fprintf(stderr, “No code generated.\n”);
    exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}

Теперь вы можете поместить этот файл в репозиторий в качестве ревизии 1.7.

$ cvs commit -m “Initialize scanner. Use symbolic exit values.” driver.c
Checking in driver.c;
/usr/local/cvsroot/yoyodyne/tc/driver.c,v  <–  driver.c
new revision: 1.7; previous revision: 1.6
done

Чтобы защитить вас, CVS откажется фиксировать файл, если в нем произошел конфликт и вы с ним не справились. В настоящий момент для разрешения конфликта нужно изменить дату модификации файла. В предыдущих версиях CVS вам также требовалось убедиться, что файл не содержит маркеров конфликта. Так как ваш файл действительно может содержать маркеры конфликтов (символы `>>>>>>>>’ в начале строки, не обозначающие конфликта), то в текущей версии CVS выдает предупреждение и фиксирует файл.

Если вы используете pcl-cvs (оболочка к CVS для Emacs) версии 1.04 или позже, вы можете использовать пакет emerge, помогающий разрешать конфликты. Смотрите документацию по pcl-cvs.

Информирование коллег о фиксировании ревизий

Часто бывает полезно информировать своих коллег, когда вы фиксируете новую ревизию файла. Можно использовать ключ `-i’ в файле `modules’ или файле `loginfo’ для автоматизации этого процесса. See section Файл `modules’. See section Файл loginfo. Можно использовать эти возможности CVS для того, чтобы указать CVS, например, отсылать почтовые сообщения всем разработчикам или помещать сообщение в локальную группу новостей.

Совместный доступ нескольких разработчиков к CVS

Если несколько разработчиков попытаются одновременно выполнить CVS, один из них получит такое сообщение:

[11:43:23] waiting for bach’s lock in /usr/local/cvsroot/foo

CVS попытается повторить операцию каждые 30 секунд, и либо успешно выполнит ее, либо опять напечатает сообщение, если опять нужно ждать. Если блокировка сохраняется слишком долго, найдите того, кто создал ее и спросите его, что за команду он выполняет. Если он не выполняет команд CVS, загляните в каталог репозитория, упомянутый в сообщении, и удалите файлы, чьи имена начинаются с `#cvs.rfl’, `#cvs.wfl’, или `#cvs.lock’, принадлежащие указанному пользователю.

Заметьте, что эти блокировки предназначаются для защиты внутренних структур данных CVS и не имеют никакого отношения к слову блокировка в том смысле, который используется в RCS и означает блокированные извлечения (see section Несколько разработчиков).

Неограниченное количество пользователей может одновременно читать репозиторий; только когда кто-либо пишет туда, блокировки препятствуют прочим как читать, так и писать.

Можно было бы надеяться, что верно следующее утверждение:

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

К сожалению, при работе с CVS это утверждение неверно. Например, если имеются файлы

a/one.c
a/two.c
b/three.c
b/four.c

и кто-то выполняет

cvs ci a/two.c b/three.c

а затем кто-то еще в то же самое время выполняет cvs update, то он может получить изменения для `b/three.c’, и не получить изменения в `a/two.c’.

Как отследить, кто редактирует файлы?

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

Для получения максимального преимущества разработчики должны использовать cvs edit (а не chmod), чтобы сделать файлы доступными для чтения/записи, и cvs release (а не rm) для удаления рабочего каталога, который более не используется. CVS не может вынудить их делать так.

Как с помощью CVS следить за определенными файлами?

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

Команда: cvs watch on [-lR] файлы …

Команда означает, что разработчикам нужно выполнить команду cvs edit перед редактированием файлов. Для того, чтобы напомнить об этом разработчикам, CVS создаст рабочие копии файлов в режиме только для чтения.

Если среди файлов есть имя каталога, CVS включает слежение за всем файлами, находящимися в соответствующем каталоге репозитория и автоматически включает режим слежения за всеми файлами, которые будут в дальнейшем добавлены в каталог; это позволяет пользователю задать стратегию уведомлений для каждого каталога. Содержимое каталога обрабатывается рекурсивно, если только не задан ключ командной строки -l. Ключ -R позволяет включить рекурсивное поведение, если в файле `~/.cvsrc’ оно было выключено с помощью ключа -l (see section Ключи по умолчанию и файл ~/.cvsrc).

Если список файлов пропущен, по умолчанию обрабатывается текущий каталог.

Команда: cvs watch off [-lR] файлы …

Команда означает, что при извлечении не нужно создавать файлы в режиме только для чтения; таким образом, разработчики не будут получать напоминания о необходимости использования cvs edit и cvs unedit. CVS будет извлекать файлы в обычном режиме, если только на файле не установлены специальные права доступа, разрешенные с помощью ключевого слова PreservePermissions в административном файле `config’; см. section Специальные файлы, а также See section Файл конфигурации CVSROOT/config.

Файлы и ключи командной строки обрабатываются точно так же, как и для cvs watch on.

CVS может посылать вам уведомления

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

Команда: cvs watch add [-a действие] [-lR] файлы …

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

Ключ командной строки -a задает тип событий, о которых следует посылать уведомления. действие – это

edit
Другой пользователь выполнил для файла команду cvs edit (описанную ниже).
unedit
Другой пользователь выполнил команду cvs unedit (описанную ниже) или команду cvs release, или удалил файл и позволил команде cvs update создать его заново.
commit
Другой пользователь зафиксировал изменения в файле.
all
Все эти действия.
none
Ни одно из этих действий. (Это полезно вместе с cvs edit, описанной ниже.)

Ключ -a можно указать несколько раз или вообще не указывать, в этом случае по умолчанию используется all.

Файлы и ключи командной строки обрабатываются так же, как и в команде cvs watch.

Команда: cvs watch remove [-a действие] [-lR] файлы …

Удалить запрос на уведомление, созданный с помощью cvs watch add; аргументы те же самые. Если присутствует ключ командной строки -a, то только удаляются только слежения за указанными действиями.

Когда требуется отправить уведомление, CVS обращается к административному файлу `notify’. Этот файл можно отредактировать точно так же, как и другие административные файл (see section Административные файлы). Синтаксис этого файла подобен другим административным файлам (see section Обычный синтаксис), где каждая строка состоит из регулярного выражения и команды, которую надо выполнить. Команда должна содержать одно единственное упоминание символов `%s’, которые будут заменены на имя пользователя, которого нужно уведомить; остальная информация передается этой команде на стандартный вход. Обычно в файл `notify’ помещается такая строка:

ALL mail %s -s "CVS notification"

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

Заметьте, что если вы настроите все именно так, как рассказано выше, то пользователи будут получать уведомления на сервере. Конечно же, можно написать скрипт `notify’, который перенаправляет уведомления на другой адрес, но, для простоты, CVS позволяет задать адрес, по которому следует отсылать уведомления пользователю. Для этого создайте в `CVSROOT’ файл `users’, в котором каждая строка имеет вид пользователь:адрес. Тогда вместо того, чтобы использовать имя пользователя, CVS будет использовать адрес.

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

Как редактировать файлы, за которыми наблюдают?

Так как файл, за которым следит кто-либо, извлекается в режиме только для чтения, то вы не можете просто взять и отредактировать его. Для того, чтобы сделать его доступным для записи и сообщить остальным, что вы планируете отредактировать этот файл, используйте команду cvs edit. Некоторые системы называют это извлечение, но пользователи CVS уже используют этот термин в смысле “получение копии исходных текстов” (see section Получение исходного кода), а эту операцию, в свою очередь, другие системы называют взять.

Команда: cvs edit [ключи] файлы …

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

Команда cvs edit принимает такие же ключи командной строки, что и команда cvs watch add, и устанавливает временное слежение за файлами для пользователя; CVS прекратит слежение, когда будет выполнена команда unedit или команда commit. Если пользователь не хочет получать уведомления, он должен указать ключ -a none.

Файлы и ключи командной строки обрабатываются точно так же, как и для команды cvs watch.

Предупреждение: если в репозитории разрешена опция PreservePermissions (see section Файл конфигурации CVSROOT/config), то CVS не будет менять прав доступа к файлам. Причина этого изменения – убедиться, что cvs edit не мешает хранению прав доступа к файлам в CVS-репозитории.

Обычно, когда вы закончите редактирование файлов, используйте команду cvs commit, которая проверит ваши изменения и вернет файлы, за которыми производилось слежение, в обычное состояние только для чтения. Если же вы вместо этого решите отменить изменения, или просто не станете ничего менять, используйте команду cvs unedit.

Command: cvs unedit [-lR] files …

Отбросить все изменения в рабочих файлах files и привести их в соответствие с ревизией в репозитории. Если кто-либо запросил уведомление об изменениях каких-либо файлов, то CVS делает эти файлы доступными только для чтения. CVS уведомляет пользователей, которые запросили уведомление о команде unedit.

Ключи командной строки и список файлов обрабатываются точно так же, как для команды cvs watch.

Если слежение не используется, команда unedit, вероятно, не работает, и единственный способ вернуть файл в то состояние, в котором он находится в репозитории – удалить его и использовать cvs update для получения новой копии. Семантика этой операции идентична команде unedit: удаление и обновление может внести также и изменения, которые были помещены в репозиторий с тех пор, как вы в последний раз обновляли свою рабочую копию.

При использовании сетевого CVS вы можете использовать команды cvs edit и cvs unedit, даже если CVS не смогла успешно соединиться с сервером. Уведомления будут посланы при следующем успешном выполнении какой-либо команды CVS.

Информация о том, кто следит и кто редактирует

Команда: cvs watchers [-lR] files …

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

Ключи командной строки и список файлов обрабатываются так же, как и в команде cvs watch.

Команда: cvs editors [-lR] files …

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

Список файлов и ключи командной строки обрабатываются точно так же, как и в команде cvs watch.

Использование слежений со старыми версиями CVS

Если вы используете возможность слежения за репозиторием, то в нем создаются каталоги `CVS/’, в которых хранится информация о слежениях за файлами из соответствующего каталога. Если вы попытаетесь использовать в этом репозитории CVS версии 1.6 и ранее, вы получите такое сообщение об ошибке:

cvs update: cannot open CVS/Entries for reading:
No such file or directory

Выполняемая команда, скорее всего, будет прервана. Для использования возможности слежения обновите все копии CVS, которые используют этот репозиторий в локальном или серверном режиме. Если вы не можете совершить обновление, используйте команды watch off и watch remove, чтобы удалить все слежения, а затем восстановите репозиторий в состояние, с которым может работать @cvsver{1.6}.

Выбор между блокированными и неблокированными извлечениями

Блокированные и неблокированные извлечения имеют свои “за” и “против”. Достаточно сказать, что это в основном вопрос мнения, или же принятого в группе стиле работы. Впрочем же, вот краткое описание некоторых возникающих вопросов. Существует множество способов организовать команду разработчиков. CVS не пытается насильно внедрить какой-либо способ. CVS – это просто инструмент, который можно использовать различными способами.

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

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

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

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

Возможность слежения, описанная выше, в главе section Как отследить, кто редактирует файлы?, может считаться промежуточной моделью между блокированными и неблокированными изменениями. Когда вы редактируете файл, можно узнать, кто ещё редактирует его. Система, вместо того, чтобы просто запретить двум людям редактировать файл, может описать ситуацию и позволить вам самому решить, является ли этот конкретный случай проблемой или нет. Таким образом, для некоторых групп механизм слежения может считаться объединением лучших черт блокированных и неблокированных изменений.


Go to the first, previous, next, last section, table of contents.

Comments