Вы все еще храните копии разных версий своих программ? Неверное, вы еще не знаете, что такое CVS (Concurrent Versioning System).
CVS — это система, которая позволяет вести историю изменений ваших файлов. Вы можете использовать ее вместо того, чтобы постоянно хранить кучу резервных копий разных их версий, занимать место на винчестере и вносить неразбериху. CVS хранит все в одном файле, в котором сохраняются лишь изменения, произошедшие с файлом.
Спектр применения CVS довольно широк. Наиболее очевидная модель ее использования — хранение исходных кодов программ. Но ничто не мешает вам с помощью CVS отслеживать изменения в разнообразных конфигурационных файлах или текстовых документах, например, в форматах TXT, HTML, LyX, LaTeX и многих других.
Как театр начинается с вешалки, так и работа с CVS начинается с создания репозитария. Репозитарий — это место, где CVS хранит файлы, которые находятся у нее под контролем. Все манипуляции с файлами вы будете производить в рабочем каталоге, а потом синхронизировать содержимое каталога с репозитарием. Для того, чтобы указать CVS путь к репозитарию, нужно указать путь к нему в переменной окружения $CVSROOT. Для bash это выглядит примерно так:
CVSROOT=/var/cvsroot export CVSROOT
Путь, разумеется, можете указать свой. Далее нужно выполнить команду, которая инициализирует репозитарий, поместив туда свои служебные файлы.
$ cvs init
Вот репозитарий и готов — можно приступать к работе. Предположим, у нас уже есть файлы, изменения в которых мы хотим отслеживать с помощью CVS, и расположены они в каталоге ~/programs/proj1. Так как мы хотим отслеживать изменения только в файлах, содержащих исходники, то неплохо было бы сначала очистить рабочий каталог от скомпилированных и объектных файлов, которые все равно создадутся при следующей компиляции, — они лишь засорят ваш репозитарий. Очистили? Заходим в него и даем команду:
$ cvs import proj1 me imp-0
Рассмотрим ее подробнее. import — это команда, которая предписывает CVS импортировать файлы из текущего каталога в репозитарий, proj1 — имя каталога, в котором будут храниться файлы в репозитарии (фактически это название проекта, к которому относятся файлы), me — так называемый тег производителя, imp-0 — тег релиза. Что такое тег в контексте CVS, мы рассмотрим ниже. При выполнении этой команды вы попадете в редактор, где неплохо бы внести описание производимой операции. Вообще CVS предлагает таким образом «комментировать» большинство операций, связанных с репозитарием, и должен заметить, не следует пренебрегать этим.
Таким образом мы поместили в репозитарий файлы из каталога proj1. Переименуйте его, например, в proj1.bak и дайте команду
$ cvs checkout proj1
Вы должны увидеть появившийся каталог proj1 с вашими файлами, а также служебный каталог CVS. Если все в порядке, можете удалить proj1.bak. Теперь поглядим на возможности работы с конкретными файлами. Допустим, в проекте есть программа hw.c:
#include int main() { printf("Hello UNIX World!\n"); }
Внесем в нее некоторые изменения:
#include int main() { printf("Hello UNIX World!\n"); return 0; }
Теперь можем сравнить, какие же мы внесли изменения. Для этого введем команду
$ cvs diff -u hw.c
и получим что-то вроде этого:
Index: hw.c ========================================= RCS file: /home/serge/cvsroot/ctmp/hw.c,v retrieving revision 1.2 diff -u -r1.2 hw.c --- hw.c 9 Mar 2003 17:43:20 -0000 1.2 +++ hw.c 9 Mar 2003 17:43:44 -0000 @@ -2,4 +2,5 @@ int main() { printf("Hello UNIX World!\n"); +return 0; }
Здесь мы видим, что в нашем файле после строки
printf("Hello UNIX World!\n");
была добавлена:
return 0;
Допустим, это обновление настолько значительное, что его обязательно нужно увековечить в репозитарии. Для этого выполним команду, которая внесет изменения в репозитарий.
$ cvs commit hw.c
Если вы внимательно наблюдали за выводимыми сообщениями, то могли заметить, что теперь ваш файл имеет новую версию 1.2. Теперь в рабочем каталоге создадим еще один файл. Например, Makefile. Теперь нам нужно добавить этот файл в проект. Даем команду add для Makefile.
$ cvs add Makefile
А потом подтверждаем добавление с помощью:
$ cvs commit
В процессе написания программы мы можем дойти до определенного этапа, когда необходимо отметить текущее состояние разработки с помощью так называемых тегов . Определенный тег объединяет ряд версий файлов. Обратившись к тегу, мы получим все относящиеся к нему версии файлов. Установить теги можно командами tag и rtag. Отличие между ними заключается в том, что первая объединяет те версии файлов, которые использовались при извлечении файлов в рабочий каталог, а rtag — последнюю commit'ченую версию, независимо от рабочего каталога.
$ cvs rtag 1-1-STABLE proj1
1-1-STABLE — это идентификатор тага, здесь можно набрать все что угодно, кроме символа точки. Теперь мы сможем в любой момент вернуться к состоянию тега 1-1-STABLE. Получить файлы из 1-1-STABLE можно командой checkout, через опцию -r ей передается дополнительный параметр — идентификатор тега. Например, вот так:
$ cvs checkout -r 1-1-STABLE proj1
Кроме тега можно получить состояние на определенный момент времени с помощью опции -D, например на вчера:
$ cvs checkout -D yesterday proj1
Опцией -D мы предписываем CVS получить самую новую ревизию файла, но не позднее указанной даты. Указать эту дату можно несколькими способами: 1 month ago, last year, last Saturday, 200 seconds ago, 4/28/2003 11:00, April 28, 2002 11:00am.
При выполнении checkout создается каталог с названием проекта (в нашем случае proj1) и в него помещаются файлы. Если нужно только обновить данные в текущем рабочем каталоге, необходимо воспользоваться командой update. Для этого нужно зайти в каталог с рабочей копией:
$ cd proj1 $ cvs update
Для update можно применять те же опции, что и для checkout, например -r и -D. С другой стороны, получение версии более старой, чем вершина основного ствола (см. рис.) чревато установкой так называемых «липких меток». Эти метки не позволят закоммитить изменения из файла с липкой меткой в основной. Например, если есть файл file1.c, который на данный момент имеет версию 1.12, и мы получили его версию 1.4, то нижеприведенная команда для версии 1.4 не пройдет.
$ cvs commit file1.c
Проверить наличие липких меток можно командой status для всех файлов в рабочем каталоге
$ cvs status
и для файла file1.c.
$ cvs status file1.c
Избежать появления нескольких меток можно, только пользуясь некоторыми нестандартными увертками. Я остановлюсь лишь на одной, которая, ИМХО, наиболее практична. Необходимо воспользоваться опцией –j, которая используется для слияния ветвей и ревизий. Чтобы получить старую версию файла без липких меток, нужно стереть в рабочем каталоге текущую версию файла и дать команду:
$ cvs update -j 1.4 file1.c
В результате получим версию 1.4 файла file1.c без липкой метки. Вообще говоря, липкие метки придуманы не просто так, и если они у вас появляются, значит вы отступаете от принятых правил ведения версий, что не есть хорошо.
Возвращаясь к созданию тегов, могу посоветовать не тегить каждую новую версию файлов — это глупо. Лучше всего объединять тегами ключевые этапы разработки, а также релизы вашей программы.
Допустим, мы развивали программу уже довольно долго и пора бы ей показаться на люди. Для того, чтобы получить файлы без административной информации, у CVS существует команда export.
$ cvs export -r 1-0-RELEASE
В качестве параметра обязательно нужно передать тег или дату, относительно которых будет произведен экспорт. Хватаете исходники, собираете и продаете подороже, как завещал великий дядюшка Билл :-).
Но это еще не все — теперь мы продолжаем работать над второй версией программы. Чтобы отметить это важнейшее событие, можно сделать так, чтобы в номерах ревизий отражался новый виток в разработке. Здесь нам поможет команда commit, которой в качестве параметра можно передавать номер ревизии.
$ cvs commit -r 2.0
Мы работаем над новой версией программы, но от ее юзверей то и дело поступает информация об ошибках, а до новой версии еще далеко. Ничего страшного — для этого в CVS существуют ветви. Берем за основу наш релиз 1-0-RELEASE и создаем на его основе ветвь поддержки. Опять используем команду tag, но с ключом -b.
$ cvs tag -b -r 1-0-RELEASE 1-0-patches
В этом примере -r 1-0-RELEASE можно опустить, но тогда ветвь создастся по текущему состоянию рабочего каталога, что в нашем случае не очень хорошо, ведь мы договорились, что разработка пошла дальше. Название создаваемой ветви (у нас 1-0-patches) — это тоже своеобразный тег, поэтому названия обычных тегов и веток не должны совпадать. Извлечь созданную ветку можно как в новый каталог, так и в существующий. Для получения ветки используются все те же команды checkout и update.
$ cvs checkout -r 1-0-patches proj1
или команда, выполненная в рабочем каталоге
$ cvs update -r 1-0-patches
При связывании рабочей копии с веткой эта связь будет оставаться пока вы ее не отмените — все вносимые изменения будут отражаться только на ветви. Ветви также полезны для экспериментов с содержимым файлов. Например, программист желает попробовать внести коррективы в исходник, но не уверен в результате. Допустим, результат таки не понравился программисту. Что, теперь ручками вставлять код в основной ствол? Ну уж нет, CVS сделает все сам — уже упоминавшаяся опция -j (от англ. join — объединять) для команд checkout и update сливает воедино изменения в ветви и основном стволе (или где вы укажете).
Получаем последнюю версию проекта
$ cvs checkout proj1
Сливаем изменения с головы ветки 1-0-release-addons в основной ствол.
$ cvs update -j 1-0-release-addons
Подтверждаем изменения.
$ cvs commit
Допустим, мы продолжаем делать addon'ы и решаем опять добавить их в ствол. Используем команду вида:
$ cvs update -j 1.3.2.1 -j 1-0-release-addons
Она внесет изменения сделанные между ревизией 1.3.2.1 и головой ветки 1-0-relaease-addons.
Это далеко не все возможности CVS, но для начала работы хватит с головой. Если что не понятно, у вас всегда под рукой man cvs, а также чудесный перевод документации по CVS с ответами на многие вопросы, которые не были рассмотрены в рамках статьи, на http://cvs.ru/cvs-ru.html или в сжатом варианте http://cvs.ru/cvs-ru.zip, 130 Кб. Для работы с CVS подходит не только командная строка — можно воспользоваться и каким-нибудь фронт-эндом. Для Linux таковым, например, является Cervisa и многие другие, а для Виндов —WinCVS.

