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

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

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

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


Создание ветвей и слияние

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

Позже вы можете переместить изменения с одной ветки на другую или же с ветки в ствол, это называется слиянием. Сначала выполняется cvs update -j, чтобы слить изменения в рабочий каталог, а затем эти изменения можно зафиксировать, что фактически приведет к копированию изменений на другую ветку.

Для чего хороши ветви?

Предположим, был выпущен tc версии 1.0. Вы продолжаете его разработку, планируя выпустить версию 1.1 через пару месяцев. Через некоторое время ваши пользователи начинают жаловаться на серьезную ошибку. Вы извлекаете версию 1.0 (see section Метки ревизий) и находите ошибку, для исправления которой требуется всего лишь тривиальное изменение). Однако же, текущая версия исходников находится в крайне нестабильном состоянии и не стабилизируется по крайней мере еще месяц. Вы не можете выпустить исправленную версию, основываясь на свежих исходниках.

В подобной ситуации имеет смысл создать ветку в дереве ревизий, содержащую файлы, из которых состояла версия 1.0. Затем вы вносите изменения в ветвь без вторжения в основной ствол. Потом вы сможете либо внести те же самые изменения в основной ствол, либо оставить их только на ветви.

Создание ветви

Вы можете создать ветвь, используя cvs tag -b. Например, если вы находитесь в каталоге с рабочей копией:

$ cvs tag -b rel-1-0-patches

Это отщепляет ветку, основанную на текущей ревизии рабочей копии, и присваивает этой ветке имя `rel-1-0-patches’.

Важно понимать, что ветки создаются в репозитории, а не в рабочей копии. Создание ветки, основанной на текущей ревизии, как в вышеприведенном примере, НЕ переключает рабочую копию на использование ветки (See section Доступ к веткам, где описано, как сделать это).

Можно также создать ветку вообще без использования рабочей копии, используя rtag.

$ cvs rtag -b -r rel-1-0 rel-1-0-patches tc

`-r rel-1-0’ означает, что эта ветка имеет корневую ревизию, соответствующую метке `rel-1-0’. Это не обязательно должна быть самая последняя ревизия: довольно часто бывает полезно отщепить ветку от старой ревизии (например, для исправления ошибки в старой версии, которая в основном стабильна).

Как и в случае с `tag’, ключ командной строки `-b’ заставляет rtag создать ветку (а не символьное имя ревизии). Заметьте, что номера ревизий, соответствующих `rel-1-0’, скорее всего, будут разными в разных файлах.

Таким образом, полный эффект этой команды – создать новую ветку, которая называется `rel-1-0-patches’, в модуле `tc’, которая растет в дереве ревизий из точки, помеченной как `rel-1-0’.

Доступ к веткам

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

Для того, чтобы извлечь ветку из репозитория, выполните команду `checkout’ с ключом командной строки `-r’, с именем метки в качестве параметра (see section Создание ветви).

$ cvs checkout -r rel-1-0-patches tc

Если у вас уже есть рабочая копия, вы можете переключить ее на нужную ветку с помощью `update -r’:

$ cvs update -r rel-1-0-patches tc

или, что то же самое:

$ cd tc
$ cvs update -r rel-1-0-patches

Неважно, что рабочая копия была извлечена из основного ствола или какой-нибудь другой ветки: вышеприведенная команда переключит ее на указанную ветку. Подобно обычной команде `update’, `update -r’ сливает сделанные изменения, уведомляя вас о произошедших конфликтах.

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

Чтобы узнать, на какой ветви находится рабочая копия, можно использовать команду `status’. В том, что она вывела на экран, обратите внимание на поле, которое называется `Sticky tag’ (see section Липкие метки) – здесь CVS сообщает, на какой ветви находятся рабочие файлы:

$ cvs status -v driver.c backend.c
===================================================================
File: driver.c          Status: Up-to-date

    Version:            1.7     Sat Dec  5 18:25:54 1992
    RCS Version:        1.7     /u/cvsroot/yoyodyne/tc/driver.c,v
    Sticky Tag:         rel-1-0-patches (branch: 1.7.2)
    Sticky Date:        (none)
    Sticky Options:     (none)

    Existing Tags:
        rel-1-0-patches             (branch: 1.7.2)
        rel-1-0                     (revision: 1.7)

===================================================================
File: backend.c         Status: Up-to-date

    Version:            1.4     Tue Dec  1 14:39:01 1992
    RCS Version:        1.4     /u/cvsroot/yoyodyne/tc/backend.c,v
    Sticky Tag:         rel-1-0-patches (branch: 1.4.2)
    Sticky Date:        (none)
    Sticky Options:     (none)

    Existing Tags:
        rel-1-0-patches             (branch: 1.4.2)
        rel-1-0                     (revision: 1.4)
        rel-0-4                     (revision: 1.4)

Не смущайтесь тем, что номера ветвей для каждого файла различны (`1.7.2’ и `1.4.2’, соответственно). Метка ветви одна и та же, `rel-1-0-patches’, и все файлы действительно находятся на одной и той же ветке. Номера лишь отражают ту точку в истории файла, в которой появилась ветвь. Из вышеприведенного примера можно узнать, что перед тем, как была создана ветка, `driver.c’ претерпел больше изменений, чем `backend.c’.

Смотри section Ветки и ревизии, где подробно описано, как устроены номера ветвей.

Ветки и ревизии

Обычно история ревизий файла – это линейная возрастающая последовательность номеров (see section Номера ревизий):

       +—–+    +—–+    +—–+    +—–+    +—–+
       ! 1.1 !—-! 1.2 !—-! 1.3 !—-! 1.4 !—-! 1.5 !
       +—–+    +—–+    +—–+    +—–+    +—–+

Однако же, CVS не ограничен линейной разработкой. Дерево ревизий может быть расщеплено на ветви, где каждая ветвь – самостоятельная линия разработки. Изменения, сделанные на одной ветке, легко могут быть внесены также и в основной ствол.

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

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

                                                      +————-+
                           Branch 1.2.2.3.2 ->        ! 1.2.2.3.2.1 !
                                                    / +————-+
                                                   /
                                                  /
                 +———+    +———+    +———+
Branch 1.2.2 -> _! 1.2.2.1 !—-! 1.2.2.2 !—-! 1.2.2.3 !
               / +———+    +———+    +———+
              /
             /
+—–+    +—–+    +—–+    +—–+    +—–+
! 1.1 !—-! 1.2 !—-! 1.3 !—-! 1.4 !—-! 1.5 !  <- The main trunk
+—–+    +—–+    +—–+    +—–+    +—–+
                !
                !
                !   +———+    +———+    +———+
Branch 1.2.4 -> +—! 1.2.4.1 !—-! 1.2.4.2 !—-! 1.2.4.3 !
                    +———+    +———+    +———+

Обычно не требуется задумываться о точных деталях того, как строятся номера веток, но вот еще подробности: когда CVS создает номер ветки, он берет первое неиспользованное четное число, начиная с двойки. Поэтому, если вы хотите создать ветку от ревизии 6.4, она будет называться 6.4.2. Номера веток, заканчивающиеся на ноль (например, 6.4.0), используются для внутренних нужд CVS (see section Волшебные номера веток). Ветка 1.1.1 имеет специальное значение. See section Слежение за чужими исходными текстами.

Волшебные номера веток

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

Номера веток состоят из нечетного количества десятичных целых чисел, разделенных точками. See section Номера ревизий. Однако же, это не полная правда. Из соображений эффективности CVS иногда вставляет лишний ноль во вторую справа позицию (1.2.4 становится 1.2.0.4, а 8.9.10.11.12 становится 8.9.10.11.0.12 и так далее).

CVS довольно хорошо прячет такие “волшебные” ветки, но в нескольких местах ему это не удается:

  • Номера волшебных веток появляются в выдаче cvs log.
  • Вы не можете указать символическое имя ветки в команде cvs admin.

Можно использовать команду admin, чтобы переназначить символическое имя ветки на то, которое ожидает увидеть CVS. Например, если R4patches присвоено ветке 1.4.2 (волшебный номер 1.4.0.2) в файле `numbers.c’, можно сделать так:

$ cvs admin -NR4patches:1.4.2 numbers.c

Это сработает, только если хотя бы одна ревизия уже была зафиксирована на ветке. Будьте очень осторожны, чтобы не присвоить метку неправильному числу, так как нет способа узнать, чему была присвоена эта метка вчера (за исключением ежедневного резервного копирования).

Слияние веток

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

Ключ командной строки `-j’ означает “объединить” (join).

Представьте себе такое дерево ревизий:

+—–+    +—–+    +—–+    +—–+
! 1.1 !—-! 1.2 !—-! 1.3 !—-! 1.4 !      <- основной ствол
+—–+    +—–+    +—–+    +—–+
                !
                !
                !   +———+    +———+
 Ветвь R1fix -> +—! 1.2.2.1 !—-! 1.2.2.2 !
                    +———+    +———+

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

$ cvs checkout mod               # Извлечь последнюю ревизию, 1.4

$ cvs update -j R1fix m.c        # Слить все изменения, сделанные на ветке,
                                 # т. е. изменения между ревизиями 1.2
                                 # и 1.2.2.2, в рабочую копию файла

$ cvs commit -m “Included R1fix” # создать ревизию 1.5.

В результате операции слияния может произойти конфликт. В это случае вам сначала надо справиться с ним перед фиксированием изменений. See section Пример конфликта.

Команда checkout также поддерживает флаг `-j ветка. Можно добиться эффекта, обсуждавшегося выше, с помощью

$ cvs checkout -j R1fix mod
$ cvs commit -m “Добавлен R1fix”

многократное слияние из ветки

Мы продолжаем обсуждение примера. Теперь дерево ревизий выглядит так:

+—–+    +—–+    +—–+    +—–+    +—–+
! 1.1 !—-! 1.2 !—-! 1.3 !—-! 1.4 !—-! 1.5 !   <- ствол
+—–+    +—–+    +—–+    +—–+    +—–+
                !                           *
                !                          *
                !   +———+    +———+
 Ветвь R1fix -> +—! 1.2.2.1 !—-! 1.2.2.2 !
                    +———+    +———+

Здесь линия из звездочек представляет собой слияние ветки `R1fix’ с основным стволом, обсуждавшееся только что.

Предположим теперь, что разработка ветки `R1fix’ продолжается:

+—–+    +—–+    +—–+    +—–+    +—–+
! 1.1 !—-! 1.2 !—-! 1.3 !—-! 1.4 !—-! 1.5 !   <- ствол
+—–+    +—–+    +—–+    +—–+    +—–+
                !                           *
                !                          *
                !   +———+    +———+    +———+
 Ветвь R1fix -> +—! 1.2.2.1 !—-! 1.2.2.2 !—-! 1.2.2.3 !
                    +———+    +———+    +———+

и теперь вы опять хотите слить свежайшие изменения с основным стволом. Если бы вы просто использовали команду cvs update -j R1fix m.c, то CVS попыталась бы опять слить уже слитые изменения, что привело бы к нежелательным результатам.

Вместо этого вам нужно указать, что вы хотите слить только те изменения на ветке, что еще не были объединены со стволом. Для этого вы указываете два ключа командной строки `-j’, и CVS сливает изменения между первой и второй ревизиями. Например, в этом случае самым простым способом будет

cvs update -j 1.2.2.2 -j R1fix m.c    # Слить изменения между 1.2.2.2 и
                                      # головой ветки R1fix

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

cvs update -j R1fix:yesterday -j R1fix m.c

Еще лучше было бы помечать ветку `R1fix’ после каждого слияния со стволом, и использовать эту метку для дальнейших слияний:

cvs update -j merged_from_R1fix_to_trunk -j R1fix m.c

Слияние изменений между двумя ревизиями

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

Команда

$ cvs update -j 1.5 -j 1.3 backend.c

отменит изменения, сделанные между ревизиями 1.3 и 1.5. Обратите внимание на порядок указания ревизий!

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

Указав два ключа командной строки `-j’, можно также отменить удаление или добавление файла. Например, предположим, у вас есть файл `file1’, существовавший в ревизии 1.1. Затем вы удалили его, создав “мертвую” ревизию 1.2. Теперь предположим, что вы хотите добавить его опять, с тем же самым содержимым, что он имел ранее. Вот как сделать это:

$ cvs update -j 1.2 -j 1.1 file1
U file1
$ cvs commit -m test
Checking in file1;
/tmp/cvs-sanity/cvsroot/first-dir/file1,v  <–  file1
new revision: 1.3; previous revision: 1.2
done
$

При слиянии можно добавлять и удалять файлы

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

For example:

cvs update -A
touch a b c
cvs add a b c ; cvs ci -m “added” a b c
cvs tag -b branchtag
cvs update -r branchtag
touch d ; cvs add d
rm a ; cvs rm a
cvs ci -m “added d, removed a”
cvs update -A
cvs update -jbranchtag

После того, как эти команды выполнены, а также выполнена команда `cvs commit’, файл `a’ будет удален, а файл `d’ будет добавлен в основной ствол.


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

Comments