3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

cvs-fast-export でCVSリポジトリをGitリポジトリに変換するメモ

Last updated at Posted at 2020-01-05

諸般の事情でCVSリポジトリのインデックスを作成する必要があるので一旦Gitリポジトリを経由するのはどうかと考えた。

... 巨大なCVSリポジトリのルーズな運用に耐えるのは困難っぽいので諦めかな。。なるべく自前のRCSパーサを書くのは避けたいんだけど。。

CVSリポジトリのミラー

普通は rsync サーバを併設していることが多いのでそれを使う。例えば gcc のリポジトリであれば、

rsync -azv --delete rsync://gcc.gnu.org/gcc-cvs gcc-cvs

gccは開発を既にSubversionに移行していて、更にGitへの移行も今年を目処に進行中だけど、(EDIT: 完了した https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git )Subversionリポジトリに入っているCVS時代の履歴は破損しているので完全な歴史はCVSに埋まっている。 ... そもそもCVSはそこまでロバストな仕組みではないので完全な履歴は考古学者の如く手で繋ぎ合わせる必要がありそう。

CVSを現役で開発に使用しているものとしてはOpenBSDやNetBSDがある。

rsync -azv --delete rsync://anoncvs4.usa.openbsd.org/cvs/src openbsd-src
rsync -azv --delete rsync://rsync2.jp.NetBSD.org/netbsd-cvs/src netbsd-src

cvs-fast-export のビルド

ESRがメンテナンスしている cvs-fast-export はCVSリポジトリを読み取ってgitのfast-import形式で書き出すことができる。

動作には cvs 、ビルドには asciidocbisonflex が必要なのでそれぞれバージョンを確認して、

$ a2x --version
a2x 8.6.10
$ bison --version
bison (GNU Bison) 3.0.4
$ flex --version
flex 2.6.4
$ cvs --version

Concurrent Versions System (CVS) 1.12.13-MirDebian-25 (client/server)

普通に make でビルドする。

make

普通に make install すると /usr/local に入るが、prefixは手で指定できる。今回は /opt/cvs-fast-export/bin にインストールした。

sudo make DESTDIR=/opt prefix=/cvs-fast-export install

変換

cvsconvert コマンドが提供されているので、それにモジュールを1つ渡せばカレントディレクトリにGitリポジトリが作成される。

cvsconvert -pv gcc-cvs/gcc

cvsconvert は生成されたリポジトリのブランチやタグを実際にチェックアウトして一致を確認してくれる。このため、変換の完了にはそれなりの時間が掛かる。

CVS → Git 変換の難しさ (タイムパラドックス)

Git変換の難しさは cvs-fast-exportのman page の"RCS/CVS LIMITATIONS" 節に詳しいが、そもそものセマンティクスが大きく異なるのでCVSリポジトリをGitに変換するのはあまり簡単な問題ではない。

最大の違いは、 CVSは 原則的にファイル単位でしかバージョンを管理しない 点で、CVS → Git 変換をする際には "ファイル毎にバラバラになったコミット処理を1つに纏める" 処理が必要になる。

例えば、CVSにおけるtagは、 複数のファイルの異なるリビジョンに同じ名前を付けている だけで、ファイル間の関係性についてはどこにも記録されない。

        file1   file2   file3   file4   file5

        1.1     1.1     1.1     1.1  /--1.1*      <-*-  TAG
        1.2*-   1.2     1.2    -1.2*-
        1.3  \- 1.3*-   1.3   / 1.3
        1.4          \  1.4  /  1.4
                      \-1.5*-   1.5
                        1.6

(SCCSやCVSのような伝統的なVCSはファイル単位でリビジョン番号を振り、かつ、管理コストを節約するために連番ではなく枝番を使用している。

例えば、 file 1 のリビジョン 1.3 からブランチを作成した場合、あらたに採番されるブランチ番号 .2 と ブランチ上のファイルの新規リビジョン .1 を組み合わせて 1.2.2.1 のようなリビジョン番号が振られる。このため、ファイルのリビジョン番号は常に偶数個の番号の組合せとなる。)

例えば、 gcc の x86-64-branch ブランチ の内容は、

$ grep -r x86-64-branch: gcc-cvs/gcc/* | head
gcc-cvs/gcc/Attic/config.if,v:  x86-64-branch:1.7
gcc-cvs/gcc/COPYING,v:  x86-64-branch:1.4
gcc-cvs/gcc/COPYING.LIB,v:      x86-64-branch:1.4
gcc-cvs/gcc/ChangeLog,v:        x86-64-branch:1.467.2.10
gcc-cvs/gcc/INSTALL/README,v:   x86-64-branch:1.7
gcc-cvs/gcc/MAINTAINERS,v:      x86-64-branch:1.205.2.1
gcc-cvs/gcc/Makefile.in,v:      x86-64-branch:1.93.2.2
gcc-cvs/gcc/README,v:   x86-64-branch:1.5
gcc-cvs/gcc/boehm-gc/allchblk.c,v:      x86-64-branch:1.9
gcc-cvs/gcc/boehm-gc/doc/README.environment,v:  x86-64-branch:1.4
(後略)

COPYING ファイルであればリビジョン 1.4ChangeLog ファイルであればリビジョン 1.467.2.10 の内容を採用すれば良いことがわかる。

対して、git(や、Subversion)はディレクトリツリー全体をバージョン管理の対象としているため、リポジトリを変換するには "ある時刻時点でのリポジトリの状態" を 全てのファイルをスキャンした上で 再現しなければならない。この制約があるため、cvs-fast-exportはリポジトリに含まれるコミットの情報全てを一旦メモリに読んだ上で処理する方式を取っている。

CVSは当初RCSのフロントエンドとして作られた経緯があるため、ファイルの履歴はRCSの ,v ファイルに記録される。例えば、 MAINTAINERS ファイルの履歴は MAINTAINERS,v ファイルに記録されていて、

MAINTAINERS,v
1.406
date    2005.03.30.20.00.27;    author gerald;  state Exp;
branches;
next    1.405;

のようにdiffの日付/時刻とauthorの情報が含まれている。このファイルには

grep ^date MAINTAINERS,v |wc -l
845

845個の変更履歴が含まれているので、これを全ファイルについて時刻順にソートして変更時刻順に並べれば良いように思えるが そうは問屋が卸さない

例えば、 gcc/boehm-gc/Attic/Makefile,v ファイルを見てみると、

1.2
date	99.04.07.08.01.28;	author tromey;	state dead;
branches;
next	1.1;

1.1
date	99.04.07.14.56.06;	author tromey;	state Exp;
branches
	1.1.1.1;
next	;

なんと 1.1 (7月14日)よりも 1.2 (7月8日)の方が古い。 タイムパラドックスだ! (ついでにこの日付フォーマットには2000年問題もある)

CVSは歴史的事情で時刻はクライアントの自己申告に頼っており、オペミスやその他の要因でタイムパラドックスを含むリポジトリを作成できてしまう。Subversionやより後年のVCSでは、ディレクトリツリー全体を管理する思想によってこのような問題を防いでいる。

一応、最近(2004年)のcvsでは commitid という複数ファイルへの編集を一意に識別するためのIDが導入されていて複数ファイルに跨がるコミットを付番できるようになっているが、現役でCVSを使っているような限られたプロジェクトくらいしか恩恵が無い。(特に、gccは2005年にはSubversionに移行してしまったため1つも無い)

3
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?