|
Введение в CVSКонспект первого дня двухдневного курса по CVSJim Blandy Перевод на русский язык: Алексей Махоткин <alexm@hsys.msk.ru>,http://alexm.here.ru
Table of Contents
Для чего нужен CVS?CVS поддерживает историю дерева каталогов с исходным кодом, работая с последовательностью изменений. CVS маркирует каждое изменение моментом времени, когда оно было сделано, и именем пользователя, совершившим изменение. Обычно человек, совершивший изменение, также предоставляет текстовое описание причины, по которой произошло изменение. Вооружившись всей этой информацией, CVS может отвечать на такие вопросы, как
Как использовать CVS -- первый набросокПеред обсуждением множества разнобразных терминов и идей, давайте взглянем на основные команды CVS. Настройка вашего репозитория
CVS хранит все изменения в данном проекте в дереве каталогов, называемом
"репозиторием" (repository). Перед тем, как начать использовать CVS, вам
необходимо настроить переменную среды
В любом случае, на нашей системе репозиторий находится в
` ` setenv CVSROOT /usr/src/master '
если ваш командный интерпретатор -- ` CVSROOT=/usr/src/master export CVSROOT ' если это Bash или какой-либой другой вариант Bourne shell. Если вы забудете сделать это, CVS пожалуется, если вы попытаетесь запустить его: $ cvs checkout httpc cvs checkout: No CVSROOT specified! Please use the `-d' option cvs [checkout aborted]: or set the CVSROOT environment variable. $ Извлечение рабочего каталогаCVS не может работать в обычном дереве каталогов; наоборот, вы должны работать в каталоге, который CVS создаст для вас. Точно так же, как вы выписываете книгу из библиотеки перед тем, как забрать ее с собой, вам следует использовать команду `cvs checkout', чтобы получить от CVS рабочее дерево каталогов. Предположим, например, что вы работаете над проектом, называемым `httpc', тривиальным HTTP клиентом: $ cd $ cvs checkout httpc U httpc/.cvsignore U httpc/Makefile U httpc/httpc.c U httpc/poll-server $ Команда `cvs checkout httpc' означает "Извлечь дерево исходных текстов с именем `httpc' из репозитория, указанного в переменной окружения `CVSROOT'." CVS помещает дерево в подкаталог `httpc': $ cd httpc $ ls -l total 8 drwxr-xr-x 2 jimb 512 Oct 31 11:04 CVS -rw-r--r-- 1 jimb 89 Oct 31 10:42 Makefile -rw-r--r-- 1 jimb 4432 Oct 31 10:45 httpc.c -rwxr-xr-x 1 jimb 460 Oct 30 10:21 poll-server Большинство этих файлов -- рабочие копии исходных текстов `httpc'. Однако, подкаталог с именем `CVS' (самый первый) имеет другое назначение. CVS использует его для хранения дополнительной информации о каждом файле в этом каталоге, чтобы определять, какие изменения вы внесли в них с тех пор, как извлекли их из репозитория. Редактирование файловПосле того, как CVS создал рабочее дерево каталогов, вы можете обычным образом редактировать, компилировать и проверять находящиеся в нем файлы -- это просто файлы. Например, предположим, что мы хотим скомпилировать проект, который мы только что извлекли: $ make gcc -g -Wall -lnsl -lsocket httpc.c -o httpc httpc.c: In function `tcp_connection': httpc.c:48: warning: passing arg 2 of `connect' from incompatible pointer type $
Кажется, ` if (connect (sock, &name, sizeof (name)) >= 0) на if (connect (sock, (struct sockaddr *) &name, sizeof (name)) >= 0) Теперь компиляция должна пройти успешно: $ make gcc -g -Wall -lnsl -lsocket httpc.c -o httpc $ httpc GET http://www.cyclic.com ...здесь находится текст HTML с домашней страницы Cyclic Software ... $ Объединение изменений
Так как каждый разработчик использует свой собственный рабочий каталог,
изменения, которые вы делаете в своем каталоги, не становятся
автоматически видимыми всем остальным в вашей команде. CVS не публикует
изменений, пока они не закончены. Когда вы протестируете изменения, вы
должны "зафиксировать" (commit) их в репозитории и сделать их доступными
остальным. Мы опишем команду Однако, что если другой разработчик изменил тот же файл, что и вы, и, может быть, даже изменил те же самые строки? Чьи изменения будет использованы? Обычно ответить на этот вопрос автоматически невозможно, и CVS совершенно точно некомпетентен, чтобы принимать такие решения.
Поэтому перед тем, как фиксировать ваши изменения, CVS требует, чтобы
исходные тексты были синхронизированы со всеми изменениями, которые
сделали остальные члены группы. Команда $ cvs update cvs update: Updating . U Makefile RCS file: /u/src/master/httpc/httpc.c,v retrieving revision 1.6 retrieving revision 1.7 Merging differences between 1.6 and 1.7 into httpc.c M httpc.c $ Рассмотрим пример строка за строкой:
Так как CVS объединила чьи-то еще изменения с вашими исходными текстами, следует убедиться, что они все еще работают: $ make gcc -g -Wall -lnsl -lsocket httpc.c -o httpc $ httpc GET http://www.cyclic.com ...HTML text for Cyclic Software's home page follows... $ Фиксирование изменений
Теперь, когда вы синхронизировали свои исходники с коллегами и
протестировали их, вы готовы поместить свои изменения в репозиторий
и сделать их видимыми остальным разработчикам. Единственный файл,
который вы изменили -- это ` $ cvs update cvs update: Updating . M httpc.c $
Как и ожидалось, единственный файл, который упоминает CVS -- это
` $cvs commit httpc.c В этом месте CVS запустит ваш любимый текстовый редактори попросит вас ввести описание изменений. После того, как вы выйдете из редактора, CVS зафиксирует ваши изменения: Checking in httpc.c; /u/src/master/httpc/httpc.c,v <-- httpc.c new revision: 1.8; previous revision: 1.7 $
Заметьте, что теперь вы зафиксировали ваши изменения и они видны всем
остальным членам группы. Когда другой разработчик исполняет Отслеживание изменений
Теперь вы, возможно, захотите узнать, какие именно изменения внес другой
разработчик в файл ` $ cvs log httpc.c RCS file: /usr/src/master/httpc/httpc.c,v Working file: httpc.c head: 1.8 branch: locks: strict access list: symbolic names: keyword substitution: kv total revisions: 8; selected revisions: 8 description: The one and only source file for trivial HTTP client ---------------------------- revision 1.8 date: 1996/10/31 20:11:14; author: jimb; state: Exp; lines: +1 -1 (tcp_connection): Cast address stucture when calling connect. ---------------------------- revision 1.7 date: 1996/10/31 19:18:45; author: fred; state: Exp; lines: +6 -2 (match_header): Make this test case-insensitive. ---------------------------- revision 1.6 date: 1996/10/31 19:15:23; author: jimb; state: Exp; lines: +2 -6 ... $ Большую часть текста здесь вы можете игнорировать; следует только обратить внимание на серию журнальных записей после первой строки черточек. Журнальные записи выводятся на экран в обратном хронологическом порядке, исходя из предположения, что недавние изменения обычно более интересны. Каждая запись описывает одно изменение в файле, и может быть разобрано на составные части так:
Команда
Если вы хотите взглянуть на соответствующее изменение, то можете
использовать команду $ cvs diff -c -r 1.6 -r 1.7 httpc.c Перед рассмотрением того, что нам выдала эта команда, опишем, что означает каждая ее часть.
Вот что выдаст эта команда: Index: httpc.c ================================================================= RCS file: /u/src/master/httpc/httcp.c,v retrieving revision 1.6 retrieving revision 1.7 diff -c -r1.6 -r1.7 *** httpc.c 1996/10/31 19:15:23 1.6 --- httpc.c 1996/10/31 19:18:45 1.7 *************** *** 62,68 **** } ! /* Return non-zero iff HEADER is a prefix of TEXT. HEADER should be null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len) --- 62,69 ---- } ! /* Return non-zero iff HEADER is a prefix of TEXT, ignoring ! differences in case. HEADER should be lower-case, and null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len) *************** *** 76,81 **** --- 77,84 ---- for (i = 0; i < header_len; i++) { char t = text[i]; + if ('A' <= t && t <= 'Z') + t += 'a' - 'A'; if (header[i] != t) return 0; } $ Требуются некоторые усилия, чтобы привыкнуть к такой подаче информации, но это определенно стоит того.(2)
Интересная часть информации начинается с первых двух строк, начинающихся
с *************** *** 62,68 **** } ! /* Return non-zero iff HEADER is a prefix of TEXT. HEADER should be null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len) --- 62,69 ---- } ! /* Return non-zero iff HEADER is a prefix of TEXT, ignoring ! differences in case. HEADER should be lower-case, and null-terminated; LEN is the length of TEXT. */ static int match_header (char *header, char *text, size_t len)
Текст из более старой редакции находится после строки Вот второй "ломоть": *************** *** 76,81 **** --- 77,84 ---- for (i = 0; i < header_len; i++) { char t = text[i]; + if ('A' <= t && t <= 'Z') + t += 'a' - 'A'; if (header[i] != t) return 0; }
Здесь описывается добавление двух строк, что обозначается символами
`
Как и выход команды Добавление и удаление файловCVS обращается с добавлением и удалением файлов так же, как и с прочими изменениями, записывая такие события в истории файлов. Можно смотреть на это так, как будто CVS сохраняет историю каталогов вместе с историей файлов.
CVS не считает, что созданные файлы должны оказаться под его контролем;
это не так во многих случаях. Например, не требуется записывать историю
изменений объектных и выполняемых файлов, потому что их содержимое
всегда может быть воссоздано из исходных файлов (надо надеяться). Вместо
этого, когда вы создадите новый файл,
Чтобы добавить файл в проект, сначала вы должны создать его, затем
использовать команду $ ls CVS Makefile httpc.c poll-server $ vi README ...введите описание httpc... $ ls CVS Makefile README httpc.c poll-server $ cvs update cvs update: Updating . ? README --- CVS еще не знает об этом файле $ cvs add README cvs add: scheduling file `README' for addition cvs add: use 'cvs commit' to add this file permanently $ cvs update --- что же теперь думает CVS? cvs update: Updating . A README --- Файл помечен как добавленный $ cvs commit README ... CVS просит вас ввести журнальную запись... RCS file: /u/jimb/cvs-class/rep/httpc/README,v done Checking in README; /u/src/master/httpc/README,v <-- README initial revision: 1.1 done $
CVS обращается с удаленными файлами почти так же. Если вы удалите файл и
выполните `cvs update', CVS не считает, что вы намереваетесь
удалить файл из проекта. Вместо этого он поступает милосерднее -- он
восстанавливает последнюю сохраненную в репозитории версию файла и
маркирует его флагом Чтобы удалить файл из проекта, вы должны сначала удалить его,а потом использовать команду `cvs rm', чтобы пометить его для удаления. При следующем запуске команда `cvs commit' удалит файл из репозитория. Фиксирование файла, маркированного с помощью `cvs rm' не уничтожает историю этого файла -- к ней просто добавляется еще одна редакция --- "не существует". В репозитории по прежнему хранятся все записи об этом файле, и к ним можно обращаться по желанию -- например, с помощью `cvs diff' или `cvs log'. Для переименования файла существует несколько стратегий; самая простая --- переименовать файл в рабочем каталоге, затем выполнить `cvs rm' со старым именем и `cvs add' с новым. Недостаток этого подхода в том, что журнальные записи о содержимом старого файла не переносятся в новый файл. Другие стратегии позволяют избежать этого, но зато доставляют другие, более странные проблемы. Вы можете добавлять каталоги точно также, как и обычные файлы. Написание хороших журнальных записейЕсли можно использовать `cvs diff', чтобы получить точное содержание любого изменения, то зачем тогда придумывать еще журнальную запись о нем? Очевидно, что журнальные записи короче, чем тексты изменений, и позволяют читателю получить общее понимание изменения без необходимости углубляться в детали. Однако же, хорошая запись в журнале описывает причину, по которой было сделано изменение. Например, плохая журнальная запись для редакции 1.7 может звучать как "Преобразовать `t' к нижнему регистру". Это правильно, но бесполезно -- `cvs diff' предоставляет точно ту же информацию, и гораздо яснее. Гораздо лучшей журнальной записью было бы "Сделать эту проверку независящей от регистра", потому что это гораздо яснее описывает причину любому, кто понимает, что происходит в коде --- клиенты HTTP должны игнорировать регистр букв при анализе заголовков ответа от сервера. Обработка конфликтовКак уже упоминалось, команда `cvs update' объединяет изменения, сделанные другими разработчиками, с исходными текстами в вашем рабочем каталоге. Если вы отредактировали файл одновременно с кем-то другим, CVS объединит ваши изменения. Довольно легко представить себе, как работает объединение, если изменения были совершены в разных участках файла, но что если вы оба изменили одну и ту же строку? CVS называет эту ситуацию конфликт и предоставляет вам самому разобраться с ним. Например, предположим, что вы добавили некую проверку на ошибки в код, определяющий адрес сервера. Перед фиксированием изменений вы должны запустить `cvs update', чтобы синхронизировать ваш рабочий каталог с репозиторием: $ cvs update cvs update: Updating . RCS file: /u/src/master/httpc/httpc.c,v retrieving revision 1.8 retrieving revision 1.9 Merging differences between 1.8 and 1.9 into httpc.c rcsmerge: warning: conflicts during merge cvs update: conflicts found in httpc.c C httpc.c $
В этом случае другой разработчик изменил тот же участок файла, что и вы,
поэтому CVS жалуется на конфликт. Вместо того, чтобы напечатать Чтобы справиться с конфликтом, откройте этот файл в редакторе. CVS обозначает конфликтующий текст так: /* Look up the IP address of the host. */ host_info = gethostbyname (hostname); <<<<<<< httpc.c if (! host_info) { fprintf(stderr, "%s: host not found: %s\n", progname, hostname); exit(1); } ======= if (! host_info) { printf("httpc: no host"); exit(1); } >>>>>>> 1.9 sock = socket (PF_INET, SOCK_STREAM, 0);
Текст вашего рабочего файла появляется сверху, после символов
Когда вы решите, как именно справиться с конфликтом, уберите маркеры из кода и отредактируйте его. В этом случае, так как ваша обработка ошибок определенно лучше, то просто отбросьте чужой вариант, оставив такой код: /* Look up the IP address of the host. */ host_info = gethostbyname (hostname); if (! host_info) { fprintf(stderr, "%s: host not found: %s\n", progname, hostname); exit(1); } sock = socket (PF_INET, SOCK_STREAM, 0); Теперь протестируйте изменения и зафиксируйте их: $ make gcc -g -Wall -Wmissing-prototypes -lnsl -lsocket httpc.c -o httpc $ httpc GET http://www.cyclic.com HTTP/1.0 200 Document follows Date: Thu, 31 Oct 1996 23:04:06 GMT ... $ httpc GET http://www.frobnitz.com httpc: host not found: www.frobnitz.com $ cvs commit httpc.c Важно понимать, что именно CVS считает конфликтом. CVS не понимает семантики вашей программы, он обращается с исходным кодом просто как с деревом текстовых файлов. Если один разработчик добавляет новый аргумент в функцию и исправляет все ее вызовы, пока другой разработчик одновременно добавляет новый вызов этой функции, и не передает ей этот новый аргумент, что определенно является конфликтом -- два изменения несовместимы -- но CVS не сообщит об этом. Его понимание конфликтов строго текстуально. На практике, однако, конфликты случаются редко. Обычно они происходят потому, что два человека пытаются справиться с одной и той же проблемой, от недостатка взаимодействия между разработчиками, или от разногласий по поводу архитектуры программы. Правильной распределение задач между разработчиками уменьшает вероятность конфликтов. Многие системы контроля версий позволяют разработчику блокировать файл, предотвращая внесение в него изменений до тех пор, пока его собственные изменения не будут зафиксированы. Несмотря на то, что блокировки уместны в некоторых ситуациях, это не всегда подход лучше, чем подход CVS. Изменения обычно объединяются без проблем, а разработчики иногда забывают убрать блокировку, в обоих случаях явное блокирование приводит к ненужным задержкам. Более того, блокировки предотвращают только текстуальные конфликты -- они ничего не могут поделать с семантическими конфликтами типа вышеописанного -- когда два разработчика редактируют разные файлы.
Footnotes(1)Интересно, почему это вообще не (2)Все же лучше использовать
This document was generated on 19 October 1998 using the texi2html translator version 1.51a. |
|||||||||||||||||
With any suggestions or questions please feel free to contact us |