저자: 제니퍼 베스퍼만, 역 한동훈
CVS--버전 관리 시스템--는 변경사항에 대한 히스토리(history)를 유지관리하기 때문에 사용자가 이러한 문제에 대해서 사용자가 신경쓰지 않도록 해주는 도구다. CVS를 사용하면 오래된 버전의 파일을 가져올 수 있으며, 각각의 변경 사항을 기록할 수도 있으며, 다른 사람이 변경한 것을 덮어쓰는 것을 막을 수 있다.
CVS는 파일들을 모듈에 저장하여 유지관리한다. 모듈은 디렉토리와 관련된 파일과 서브 디렉터리를 말한다. 모듈은 하나의 디렉터리에 하나의 파일이 있는 것처럼 작은 것일 수도 있고, 이용할 수 있는 하드 디스크 공간만큼 큰 것도 될 수 있다.
관련 기사
이 글은 CVS 레포지토리 사용에 대해서 설명한 "
CVS 소개"의 다음 글이다. 여기서는 레포지토리를 구축하고 유지관리하는 법에 대해서 설명하며 CVS 관리자들이 일상적으로 겪는 문제들에 대해서 얘기할 것이다.
레포지토리 생성
레포지토리는 앞으로 변경하게 될 모든 파일과 데이터를 저장할 만큼의 충분한 디스크 공간이 있는 머신에 생성되어야한다. 그 다음에는 "스코티 원칙(Scotty principle)"을 사용하여 다시 이 크기의 두 배를 사용하도록 한다. - 프로젝트는 반드시 팽창할 것이다. 만약에 바이너리 파일을 저장하려한다면 10배를 하도록 한다. 첫번째 프로젝트가 끝난후 얼마나 많은 공간이 필요한가를 느끼게 될 것이다. 경험의 법칙에 따르면 예상되는 최종 모듈의 크기의 3배 정도에 해당하는 파티션을 레포지토리로 사용하도록 해야한다.
역주: 스코티 원칙(Scotty Principle)이라는 것은 "필요하다고 생각하는 시간의 10배들 말하라"는 것으로 프로젝트 진행중에 예기치 않은 문제들이 생길 수 있음을 뜻한다. 여기서는 이러한 원칙이 레포지토리의 공간 계획에도 해당한다는 것을 뜻한다.
|
모든 사용자가 사용하는 모든 머신에서 레포지토리가 있는 머신에 액세스 할 수 있어야한다. 레포지토리 루트 디렉터리를 생성한다. 레포지토리는 종종 /home/cvsroot나 /usr/local/cvsroot에 저장된다. CVS 레포지토리를 위한 디렉터리를 설정하려면 cvs init을 사용한다.
cvs -d /home/cvsroot init
데비안 리눅스는 데비안 설정 파일 스크립트를 이용하여 레포지토리를 구축하는 cvs-makerepos 스크립트를 갖고 있다. 보다 자세한 정보는 man cvs-makerepos를 보도록 하고, 데비안 CVS 레포지토리를 설정하는 자동화된 시스템에 대한 것은 man cvsconfig를 보도록 한다.
새로운 모듈 가져오기(Importing)
프로젝트를 CVS로 로딩하기 전에 프로젝트 구조에 대해서 생각해봐야한다. 파일이나 디렉터리를 옮기거나 이름을 변경하는 것은 파일들의 히스토리에 대한 CVS 레코드를 손상시킬 수 있다. 디렉터리를 삭제하는 것은 디렉터리에 있는 모든 파일과 서브 디렉터리에 대한 레코드를 모두 잃게 된다. 따라서 CVS는 파일이나 디렉터리를 옮기거나 이름을 변경할 수 없으며, 디렉터리를 제거할 수 없다.
초기 디렉터리 구조를 만든다 - 프로젝트가 하나의 디렉터리를 갖고 있는 경우에도 이와 같이 해야한다. 이제 히스토리 관리를 할 초기 파일들을 추가한다. 프로젝트의 루트 디렉터리에서 cvs -d cvs repository import nameofmodule vendortag releasetag 명령을 사용한다.
대부분의 경우에 벤더 태그(vendor tag)와 릴리스 태그(release tag)는 필요없다. 그러나 CVS에서는 벤더 태그와 릴리스 태그를 사용해야하므로 모듈의 이름을 벤더 태그로 사용하고, 현재 버전을 릴리스 태그로 사용하도록 한다.
/home/jenn$ cd example
/home/jenn/example$ cvs -d /home/cvsroot import example example_project ver_0-1
태그(tag) 사용
태그는 파일의 개정이나 파일들의 집합에 대한 심볼 이름이다. cvs tag는 현재 작업 디렉터리와 서브디렉터리에 있는 모든 파일들의 레포지토리 버전들에 태그를 붙인다. cvs rtag 명령은 레포지토리에 있는 파일들의 날짜(timestamp)에 기반하여 태그를 붙인다. 이 명령은 기준 날짜와 가장 가까운 파일이나 기준 날짜보다 나중에 갱신된 파일이나 디렉터리의 버전에 심볼 이름을 할당한다. 작업 디렉터리에서는 이것은 보이지 않는다.
역주: 레포지토리에 각각의 파일들을 넣어두고 관리하다보면 각각의 파일들의 버전이 달라지게 된다. 이때 각각의 파일 a, b, c의 버전이 1.7, 1.3, 2.0에 RELEASE_1_0라는 태그를 붙여두면 나중에 개발작업이 진행되더라도 항상 RELEASE_1_0 태그를 사용하여 버전 1.0 코드를 계속 가져올 수 있게 된다. 이와 같이 태그를 붙인 것을 스냅샷(snapshot)이라 한다.
태그(tag)는 꼬리표라고도 할 수 있으나 여기서는 무리하게 번역하지 않았다.
|
CVS에서는 태그에 "." 문자를 사용할 수 없다.
cvs tag tagname filename
cvs tag tagname
(현재 작업 디렉터리에 있는 모든 파일의 레포지터리에 태그를 붙인다)
cvs tag -c tagname
(레포지토리에 있는 복사본이 작업 디렉터리에 있는 복사본과 다르다면 중지한다)
예제 :
cvs/example$ cvs tag release-1-0 src/sample.c
cvs/example/src$ cvs tag release-1-0
cvs/example/src$ cvs tag -c release-1-0
태그가 있는 버전을 가져오려면 체크아웃이나 업데이트시에 -r 플래그를 사용한다. 작업중인 디렉터리에서 체크아웃이나 업데이트를 하게되면 태그가 있는 버전이 작업 디렉터리에 있는 파일들을 덮어 쓰게 된다.
cvs checkout -r tagname
cvs update -r tagname
예제:
cvs$ mkdir example-rel-1.0
cvs/example-rel-1.0$ cvs checkout -r release-1-0
또는
cvs/example$ cvs update -r release-1-0
개발 가지치기(Branching)
현재 코드를 변경하지 않고 옛 버전의 코드에 있는 버그를 수정해야하거나 프로덕션 서버의 설정을 수정하지 않고 스테이지(stage) 서버에 있는 설정을 수정할 필요가 있다면 레포지토리 모듈을 나눌 필요가 있다. 가지를 사용하면 메인 모듈에 영향을 주지 않고 메인 모듈의 다양한 버전을 저장하고 가져올 수 있다. 가지에 적용된 변경사항은 나중에 병합할 수 있다.
역주: 가지치기(branching)라는 것은 하나의 모듈을 여러버전으로 나누는 것으로, 모듈을 루트로 하여 각각의 버전들이 가치를 쳐서 내려간 모양을 뜻한다. Branch에 대해서 대부분은 가지치기로 번역하며, 일부는 의역하여 "나누다"로 번역했다.
|
가지는 다음과 같은 명령으로 만든다.
cvs tag -b branchtag
예제 :
cvs/example$ cvs tag -b release-1-0-patches
체크아웃이나 업데이트를 사용하여 가지를 가져올 수 있다. 체크아웃은 가지에 대한 새로운 디렉터리를 생성하며, 업데이트는 현재 작업 디렉터리를 가지에 있는 것으로 덮어쓴다.
cvs checkout -r branchtag
cvs update -r branchtag
예제 :
cvs/example-rel-1.0$ cvs checkout -r release-1-0-patches
cvs/example$ cvs update -r release-1-0-patches
가지는 cvs update와 cvs commit이 호출하는 충돌 해결 시스템을 사용하여 메인으로 다시
합쳐질 수 있다.
cvs checkout module
cvs update -j branchtag
예제 :
/tmp/example$ cvs checkout example
/tmp/example$ cvs update -j release-1-0-patches
위 명령은 다음과 같이 한 줄로 쓸 수 있다.
cvs checkout -j branchtag module
예제:
/tmp/example$ cvs checkout -j release-1-0-patches example
시스템이 보고하는 충돌을 해결한 다음에 cvs commit을 실행한다.
디렉터리 제거
디렉터리는 CVS 명령을 사용하여 삭제할 수 없다. 디렉터리가 더 이상 필요하지 않다면 cvs remove를 사용하여 디렉터리를 비우고, 작업 복사본(working copy)을 가져올 때 cvs update -P와 cvs checkout -P를 사용한다. -P 플래그를 사용하면 빈 디렉터리는 가져오지 않도록 할 수 있다.
역주 참고: -Pd를 사용하여 삭제된 디렉터리는 없애고(Prune), 레포지토리에 새로 추가된 디렉터리를 추가할 수 있다(d). CVS를 사용하여 텍스트 문서가 아닌 이미지와 같은 파일 이진 파일을 관리하기로 하였다면 -kb 옵션을 반드시 사용하도록 한다.
원격 CVS 서버에서 업데이트하거나 체크아웃하는 경우라면 -z3 옵션을 사용하여 데이터를 압축하여 전송할 수 있다. 데이터를 압축하여 전송하면 소스 파일이 큰 경우에 업데이트나 체크아웃 시간을 단축시킬 수 있다.
|
디렉터리를 반드시 삭제하고 싶다면 레포지토리에서 rmdir을 사용해서 디렉터리를 삭제할 수 있다. 레포지토리의 복사본에서 rmdir을 사용하여 디렉터리를 삭제한 후에는 레포지토리가 손상된 곳이 없는지 확인하도록 한다. 레포지토리에서 삭제된 디렉터리가 "Attic" 서브 디렉터리를 갖고 있다면 이전에 저장된 파일들의 복사본도 잃게 된다. 레포지토리에서 디렉터리를 삭제한 다음에는 프로젝트의 모든 사용자가 갖고 있는 작업 복사본을 삭제하도록 하고, 모듈의 복사본을 갱신하기위해 체크아웃 하도록 한다.
추가:
패치 작성
CVS로 관리하는 코드에 대해서 특정 버전을 기준으로 한 패치를 작성한다면 패치를 작성할 디렉터리로 이동하여 다음과 같이 한다.
cvs -z3 diff -u > file.diff
이 파일을 CVS를 사용하지 않는 다른 사람들에게 전달하여 그들의 소스 코드를 패치하도록 할 수 있다.
|
파일 감시(watching)와 잠금(locking)
많은 버전관리 시스템과 달리 CVS는 파일 잠금을 지원하지 않는다. - CVS는 파일에 대해서 여러명이 연속적으로 편집하는 것을 막지 않는다. 그러나 여러분이 파일들을 감시하도록 하면 CVS는 파일이 편집될 때 감시자(watcher)를 알려줄 것이다. 파일들이 감시되고 있으면, 개발자는 편집을 위해서 cvs edit를 사용하고, 편집한 파일을 릴리스하기 위해서는 cvs unedit를 사용해야한다. 감시되지 않는 파일들은 CVS에게 알리지 않고 편집할 수 있다.
파일 감시를 설정하려면 다음과 같이 사용한다.
cvs watch on (files)
cvs watch off (files)
여러분 자신을 감시자(watcher)로 설정하려면 다음과 같이 사용한다.
cvs watch add (files)
cvs watch remove (files)
또는
cvs watch add -a edit|unedit|commit|all (files)
cvs watch remove -a edit|unedit|commit|all (files)
특별한 CVS 파일 notify는 감시중인 파일이 변경될 때 무슨 일이 일어나는지 알려준다. 이것은 CVS서버에 있는 사용자의 사용자명을 메일로 전송한다. CVS 사용자가 다른 주소를 갖고 있다면 레포지토리의 CVSROOT 디렉터리에 "users" 파일을 설정하도록 한다. 항목은 ser:email 형식으로 한 줄에 입력한다.
jenn:jenn@cvs.example.com.au
CVS 보안 관리
원격 레포지토리
레포지토리가 로컬 머신에 있다면 액세스 권한과 보안은 상당히 쉽다. $CVSROOT 환경 변수를 CVS 레포지토리의 루트 디렉터리로 설정하거나 -d
옵션을 사용하여 checkout을 호출하면 된다.
레포지토리가 원격 머신에 있다면 어느 머신에 레포지토리가 있는지, 머신에 액세스하기 위해 무슨 방법을 사용할 수 있는지를 CVS에 알려줘야한다. 몇 가지 방법을 이용할 수 있지만 보안과 단순성을 위해서 SSH를 사용하는 것이 좋다. 원격 $CVSROOT를 설정하는 구문은 :method:[[user]:[password]@]hostname[:[port]]:/path/to/repository이다. 예를 들면 다음과 같다.
:ext:jenn@cvs.example.com.au:/usr/local/cvsroot
(필자의 CVS가 하는 것은 info cvs에 나와 있는 것과 조금 다르다. 필자의 경우에는 호스트와 경로 사이에 사용한 콜론과 같이 필자의 버전에서 동작하는 구문을 사용했다. 여러분의 시스템에서 동작하는 구문을 사용하도록 한다)
역주 참고: 대부분의 리눅스 배포판에서는 위 구문으로 잘 동작한다.
|
SSH를 사용하기 위해 :ext: 메소드를 사용한다. :ext: 메소드는 CVS 서버와 통신하기 위해 CVS rsh이나 rsh 호환 프로그램을 사용한다. rsh 대신에 SSH를 사용하려면 $CVS_RSH 환경 변수를 SSH로 설정해야한다. SSH는 서버와 모든 클라이언트에 설정되어야하고, SSH 키도 마찬가지로 CVS 서버와 클라이언트에 모두 있어야 한다. 사용자도 CVS 서버와 클라이언트 양쪽 모두에 사용자 이름과 비밀번호를 갖고 있어야한다. 사용자 이름이 CVS 서버와 클라이언트 모두 같다면 CVSROOT 문자열에 user@ 부분을 사용하지 않아도 된다. 표준 SSH 포트를 사용한다면 CVSROOT 문자열에서 포트 설정도 필요없다.
cvs -d :ext:cvs.example.com.au:/usr/local/cvsroot checkout sample
역주 참고: 일반적인 사용자 인증 방식을 통해서 CVS를 사용하고 싶다면 :ext: 대신에 :pserver: 를 사용하면 된다. 일단 CVS 서버에 로그인하고 나면 사용자 홈 디렉터리에 .cvspass 파일이 생성되고, 그 이후에는 암호없이 CVS 명령을 사용할 수 있다. CVS 서버가 SSH1을 사용하는 경우에는 ssh-keygen을 실행하고, SSH2를 사용하는 경우에는 ssh-keygen -t rsa를 사용하여 키를 생성한다.
|
권한
레포지토리에 있는 모든 파일들은 읽기 전용이다. 이 파일들의 권한은 변경할 수 없다. 액세스를 제어하려면 디렉터리 권한을 사용해야한다. 대부분의 관리자는 모듈에 액세스하는 사용자 그룹을 만들고, 생성한 그룹에게 해당 디렉터리에 대한 쓰기 액세스를 부여한다.
원격 레포지토리를 사용한다면 모듈에 있는 모든 디렉터리가 적절한 권한을 갖도록 하기 위해 모듈의 루트 디렉터리에 setgid 권한을 설정한다. 로컬 레포지토리를 사용한다면 레포지토리에 있는 파일과 디렉터리의 권한을 제어하기 위해 $CVSUMASK를 설정할 수 있다.
개발자 머신
프로젝트를 안전하게 하는 것은 레포지토리를 안전하게 하는 것과 개발자 머신에 있는 모든 체크아웃된 복사본을 안전하게 하는 것과 관련된다. 누군가가 휴일에 사무실에 걸어들어와서 여러분의 코드를 CD로 구워버린다면 레포지토리를 안전하게 하고 모든 전송을 적절히 암호화하는 것만으로는 부족하다. 일반적으로 개발 머신에 대해서는 물리적인 보안과 네트워크 기반 보안, 프로토타입과 데모용 복사본, 그리고 코드를 체크아웃한 모든 다른 장소들 모두를 관리해야한다.
마지막으로
CVS 레포지토리를 관리하는 것은 이미 충분히 바쁜 관리자에게 불필요한 작업처럼 보일 수도 있지만, 클라이언트가 폐기했던 기능을 다시 원하거나 설정 파일상의 작은 변경으로 인한 부작용이 당장에 보이지 않더라도 3주후에 나타나거나 할 때 레포지토리는 충분히 시간을 절약해줄 것이다.
관련자료
- man cvs
- man 5 cvs
- info cvs에는 "what CVS is"와 "what CVS is not"와 같은 좋은 글이 있다. "Repository" 섹션에서는 프로토콜에 대해서 설명한다.
- Big Scary Daemons 컬럼의 BSD Tricks: CVS
- Sourceforge에도 몇 가지 CVS 관련 글이 있으며, Sourceforge Site Docs 페이지에서 섹션 6과 7을 참고한다.
데비안 리눅스 사용자용
- man cvs-makerepos
- man cvsconfig
제니퍼 베스퍼만는 척추 마디에 실리콘 워터를 갖고 태어났다고 생각하길 좋아하지만 그녀의 부모님은 이것을 인정하지 않는다. 그는 사용자로서, 지지자로서 오픈 소스에 기여했으며, 현재 Linuxchix.org의 코디네이터다.