CVS 이야기
장우현 louis (at) mizi.co.kr
박용주
$Date: 2001/08/21 13:01:41 $
여 러명이 어떤 공동의 작업을 수행할 때 유용하게 쓸 수 있는 CVS에 관한 이야기를 하고 있습니다. 이 글에서는 아직 CVS 관리에 관한 얘기는 없으며, 순수하게 사용하는 방법만 설명하고 있습니다. 글에 대한 문의사항이 있으면 언제라도 저에게 메일주시기 바랍니다. (참고로 앞으로는 어떻게 될지 모르겠지만, 현재 이글에서는 자세한 내용을 다루고 있지 않습니다. 좀더 자세한 내용을 원하시는 분들은 CVS메뉴얼을 참고하시기 바랍니다. 대부분 CVS를 사용하고픈 사람들은 프로젝트의 소스를 받아오기 위해서, 간단하게나마 참여하고 싶어서 입니다. 그런 분들에게는 이 정도의 글으로도 충분하리라고 봅니다. 혹시 실제로 쓰다가 보면 유용한 내용인데 이 글에서 빠져있다면 언제라도 메일을 보내주세요.)
1. 일반적인 얘기들
1.1. CVS가 뭐예요?
CVS 는 소스의 버전을 관리해 주는 시스템입니다. 이 말을 다시 풀어서 말하면, 프로그램(꼭 프로그램일 필요는 없습니다. 많은 CVS문서에서는 홈페이지의 경우에도 CVS를 통해서 관리할 수 있다고 합니다.) 을 개발하다 보면 각종 파일들을 수정하게 되는데 이 파일들의 버전을 관리해 주는 시스템입니다. 물론 혼자 개발할때도 유용하게 쓰일 수 있지만, 여러 사람이 동시에 하나의 프로그램을 개발할 때 진가를 발휘합니다.
1.2. 알아야 할 용어들
꼭 알아야 사용할 수 있는건 아니지만 알아두는게 여러모로 도움이 될때가 있는 용어들이 있습니다. 대표적인 것으로 revision(이 글에서는 용어에 대해서는 영어를 그대로 쓰도록 하겠습니다. 엉뚱하게 번역을 하면 읽기가 어렵더라구요.) 번호가 있습니다.
각 각의 파일들은 자신의 revision 번호를 가집니다. 이 번호는 1.1, 1.2.3.2등과 같이 항상 짝수개의 숫자를 "."으로 연결한 형태가 됩니다. 만일 어떤 파일의 현재 revision 번호가 1.2.3.2 였고, 이 파일을 내가 수정을 했다면 현재 수정한 파일의 revision 번호는 1.2.3.3 이 됩니다. 즉 마지막 숫자가 하나 증가합니다.
또 한 프로그램을 개발하다 보면 하나의 중심되는 개발 흐름이 있고, 이와는 별도로 작은 부분에 대한 개발 흐름이 있을 수 있습니다. 이런 중심되는 개발 흐름을 main trunk라고 하며, 이와는 다른 작은 부분에 대한 개발흐름을 branch 라고 합니다. 일반적으로 branch는 뻗어나온 main trunk의 revision번호에 숫자를 두자리 덧붙여서 사용합니다. 아래그림을 보면 제가 언급한 내용을 이해할 수 있을것입니다. (아래그림은 CVS 메뉴얼에서 가져왔습니다.)
+-------------+ |
또 알아둬야 할 용어중에 repository가 있습니다. (한글로 번역하면 저장소 정도가 되겠죠. 그냥 repository라고 쓰겠습니다.) repository는 쉽게 생각해서 현재 개발중인 소스를 모아둔 곳이라고 생각하시면 됩니다. 저도 더이상은 모릅니다. 많이 알면 머리만 아파요. ^^
2. CVS 사용하기
2.1. 시나리오
설 명을 시작하기 전에 현재 독자는 다음과 같은 상황이며, 이 때문에 어쩔 수 없이 CVS를 사용해야 한다고 가정을 해 보도록 하겠습니다. 현재 독자는 프로그래머이며, Qt 2.0개발에 관심이 많습니다. 그래서 현재 개발중인 Qt 소스를 계속 지켜보고 싶으며, 가능하다면 소스를 수정해서 반영도 하고 싶습니다. 그래서 Qt 개발 홈페이지에 갔습니다. 그래서 "저도 참여하고 싶어요" 라고 메일을 썼더니, Qt 개발팀에서 아래와 같은 메일이 왔습니다.
... |
2.2. Login
CVS는 위에서 설명한 것 처럼 Concurrent Version System의 약자입니다. -.-;
그럼 CVSROOT는 뭐냐구요? 바로 위에서 이야기 했던 repository입니다. 그냥 그렇구나 하시면 됩니다. 어떻게 쓰냐구요? 그냥 환경변수 CVSROOT를 지정하시거나, cvs를 사용하실 때 지정하시면 됩니다.
export CVSROOT=:pserver:xxx@cvs.troll.no:/cvs (bash, ksh사용자) |
cvs -d :pserver:xxx@cvs.troll.no:/cvs cvs-command |
이제 하셔야 할 내용은 CVS에 로긴하는 것입니다. 보통 로긴은 아래와 같이 입력하시면 됩니다.
cvs login |
이 경우 CVS서버에 로긴을 요청하고, 설정에 따라서 비밀번호를 요구하는 경우도 있습니다. 보통의 경우 쓰기권한을 가진 경우에는 비밀번호를 요구합니다. 비밀번호는 메일등으로 전달이 되므로 잘 기억하셨다가 여기에서 입력하면 됩니다.
이렇게 한번 입력된 비밀번호는 CVSROOT와 함께 $HOME/.cvspass 파일에 저장이 되므로 다음번에는 입력할 필요가 없습니다.
2.3. Check out
여 기까지 이해하는데 문제가 없었다고 생각하고, 본격적인 일을 해 보도록 하겠습니다. 우선 가장 먼저 해야할 일이 현재 repository에 있는 소스를 꺼내오는 일입니다. 이것을 check out 이라고 합니다. 사용방법은 쉽습니다. 그냥 아래와 같이 입력하시면 됩니다. (현재 개발중인 프로그램의 이름이 qt라고 가정합니다.)
cvs co qt |
위 와 같이 입력하여 check out을 마치면 qt라는 디렉토리 아래에 소스들이 들어가 있습니다. 근데 소스 이외에 CVS란 디렉토리가 있습니다. 이것은 CVS가 사용하는 디렉토리 이기 때문에 내부 내용을 절대 지우거나, 변경하지 마세요. 문제가 생기면 책임 못 집니다.
2.4. Update
위 에서 check out한 소스코드를 보려는데, 갑자기 일이 터졌습니다. 옆에 처박아 뒀던 서버가 갑자기 말을 안 듣는거예요. 오래간만에 공부쫌 해보려고 했는데... 으˜ 열받아! 터진다. 터져! 마음 같아서는 망치로 문제가 생긴 서버를 요절내고 싶지만 딸린 처자식(물론 저는 아닙니다. ^^) 얼굴들이 생각나서 착한 내가 참는다는 마음으로 서버를 손보기 시작했습니다. 아˜ 그렇게 세월은 흘러갔습니다.
며칠이 지난후 여유가 생겨서 소스코드를 컴파일 해 보려고 자리에 앉았습니다. 순간 이런 생각이 들더군요. 며칠동안 좀더 업그레이드가 되었을꺼야! 그래! 결심했어. 다시 받아오는거야! -.-; 소스코드가 몇줄이 안되고, 가까운 서버에 있는 경우에는 문제가 없지만 만일 소스코드가 엄청나게 크고, 서버는 아주멀리 있는 경우에는 어느세월에 다시 check out을 할까요? 이때는 update명령을 사용하면 됩니다. update하고 싶은 디렉토리에 들어가서 단순히 아래와 같이 입력하시면 됩니다.
cvs up |
만 일 qt의 바뀐 소스를 모두 받아오려면 qt디렉토리에서 수행하면 되며, 그게 아니라 example만 다시 받아오고 싶다면 qt디렉토리 아래에 있는 example디렉토리에서 위의 명령을 입력하면 됩니다. 하나의 파일만 update하려면 파일이름을 마지막에 지정하시면 됩니다. 없다면 현재 디렉토리 전체를 update합니다. up명령을 사용하면 아래와 같은 메시지를 출력하면서 update를 수행합니다.
? Makefile |
위 에서 관심있게 보셔야 할 내용으로 첫번째 칼럼에 나오는 status입니다. 여기에는 여러가지 내용들이 알파벳 한글자로 나타나는데 여기에서 보이는 U 는 Updated의 의미입니다. 즉 자신의 로컬 하드디스크에 있는 소스의 버전보다 repository에 있는 파일의 버전이 높아서 다시 받아왔습니다. 그러나 메뉴얼에서는 위와 같이 설명을 하고 있지만, 제가 실제로 해 본 결과로는 repository에는 있는데 현재 로컬 하드디스크에는 없는경우 U라고 나왔고, 그렇지 않고 repository에 있는 파일의 버전이 높아서 다시 받아온 경우에는 P라고 나왔습니다. 복잡하게 생각하지 말고 U, P의 경우에는 문제없이 repository에 있는 소스를 가져왔다고 생각하시면 됩니다.
그럼 첫번째 줄에 있는 ?는 뭘까요? 이건 repository에는 없는 파일인데 로컬하드에는 있는 파일이라는 말입니다. 여기에서 Makefile은 로컬에서 생성한 파일이기 때문에 그렇습니다. 그냥 무시하시면 됩니다.
이 것 이외에 중요한 status로는 M과 C가 있습니다. M은 repository에는 변화가 없는데 자신의 로컬 디스크에 있는 소스는 변한 경우입니다. 보통 개발자가 기능을 추가하기 위해서 소스를 수정한 경우입니다. 이 내용은 나중에 설명드릴 Commit으로 repository의 내용을 갱신시킬 수 있습니다. M 이외에 주의해서 봐야 할 status로 C가 있습니다. C는 Conflict의 의미로, 로컬 디스크의 파일도 변했고 repository의 내용도 변했으며 이 둘을 합칠 수 없는경우입니다. 보통 비슷한 부분을 두 명 이상의 개발자가 고친 경우입니다. 이 경우에는 update한 후 파일을 다시 수정해서 Commit해야 합니다. 이것 외에도 몇가지가 있지만 잘 사용되지 않으므로 필요한 분은 CVS메뉴얼을 읽어보시기 바랍니다.
2.5. Commit
받 아온 소스를 열심히 컴파일 했습니다. 그래서 실행해 보니 짠! 하고 수행되는데 뭔가가 이상하더군요. 그래서 열심히 소스를 분석했습니다. 그래서 문제를 찾았죠. 장하다 대한의 건아! -.-; (오늘이 날씨도 좋은 5월 5일 어린이 날인데, 놀지도 못하고 회사에 있어서 맛이 점점 가고 있습니다. 이해를 해 주시길...)
문제가 되는 부분을 수정하고 이 내용을 repository로 보낼려면 commit을 수행하면 됩니다. commit하기 전에는 반드시 update를 해 보시기 바랍니다. 내가 아닌 누군가가 또 다시 소스를 바꿀 수 있습니다. update를 수행해서 conflict가 된 경우에는 바르게 고치고 다시 commit을 시도하셔야 합니다. commit하는 방법은 아래와 같습니다.
cvs ci filename |
위 와 같이 실행하면 갑자기 에디터 화면이 뜹니다. 바로 고친 내용을 적으라고 뜨는겁니다. 적당히 자신이 수정한 내용을 적은 후 저장하고 에디터를 빠져나오면 실재로 commit작업이 수행됩니다. 메시지를 잘 보시면 자신이 수정한 소스파일의 버전이 어떻게 변하는지 볼 수 있습니다.
어떤 사람의 경우 현재 뜨는 에디터가 마음에 안드는 경우가 있습니다. cvs는 기본적으로 CVSEDITOR 환경변수가 있는지 확인한 후 있으면 이 변수에 지정된 에디터를 띄우고, 없다면 EDITOR환경변수를 사용합니다. EDITOR환경변수마저도 없다면 vi를 사용합니다. 또한 에디터를 사용하지 않고, cvs에서 -m옵션을 사용하셔서 change내용을 적을 수도 있습니다.
cvs ci -m "나 금방 수정했다. 랄랄라~" filename |
2.6. Diff
하 지만 보통의 경우에는 CVS에 읽기권한만 있는 경우가 많습니다. 이 경우에는 CVS에 쓰기권한이 있는 사람에게 메일을 통해서 바뀐내용을 보내줘야 하는데 이때 사용할 수 있는 명령어로 diff가 있습니다. 현재 수정한 파일이 driver.c 이며, 이 파일의 diff를 만들려면 아래와 같이 입력하시면 됩니다.
cvs diff driver.c > driver.c.diff |
현재 디렉토리 전체의 diff를 만들려면? 물론 파일이름을 생략하시면 됩니다.
diff 명령의 경우에는 이와같은 용도로 사용할 수도 있지만 cvs repository의 변경된 내용을 확인하고 싶을때도 유용하게 사용됩니다.
2.7. Clean
더이상 qt에 관심이 없어서 그만 사용하고 싶다면 어떻게 할까요? 가장 쉬운 방법은 qt디렉토리를 지워버리면 됩니다. -.-; 무식하긴 하지만 많이들 이렇게 사용합니다. ^^
좀 더 세련된 방법으로는 release 명령을 사용할 수 있습니다. 만일 아래와 같이 입력한 경우 현재 수정된 파일이 있는지 찾아주고, 디렉토리의 내용도 지워주므로 유용하게 사용하실 수 있습니다. (이 명령을 쓸 일은 거의 없습니다. 저도 메뉴얼에서만 봤을뿐, 실재로 사용해 본적은 한번도 없습니다. 저 역시 rm명령어를 애용합니다. ^^)
출처 : http://kldp.org/KoreanDoc/html/CVS-KLDP/index.html
3. CVS서버 설치하기
3-1. 리눅스에 설치하기 - cvspserver 를 통해 서비스하기
3-2. NT에 설치하기
4. 저장소 ( Repositery)
CVS의 저장소는 버전관리하는 모든 파일과 디렉토리들의 완벽한 복사본을 저장한다.
보통 저장소안에 있는 파일을 직접 조작하지 않는다. 그대신 CVS명령어를 이용해 작업디렉터리에 카피본을 만들고, 그 카피본으로 작업을 하게된다. 수정이 되면 그것을 저장소로 반영시켜 넣는다. 그러면 저장소는 수정된 내용을 포함할 뿐 아니라, 무엇을 수정했는지에 관한 정확한 기록도 포함한다. 저장소가 작업디렉터리의 서브디렉터리가 아니라는 것에 주의해라. 저장소와 작업디렉터리는 다른 위치에 존재해야 한다.
CVS는 저장소를 다양한 방법으로 접근할 수 있다. 저장소는 로컬컴퓨터일수도 있고, 아니면 방건너에 있거나 다른 나라에 있을수도 있다. 저장소를 접근하는 다양한 방법을 구분하기위하여, 저장소이름은 접근방으로 시작한다. 예를들면 :local:
란 것은 저장소 디렉터리를 의미하는 접근방법이다. 따라서 :local:/usr/local/cvsroot
는 저장소가 같은 컴퓨터의 `/usr/local/cvs'
에 있다는 것을 의미한다. 다른 접근방법에 관해서는 아래에서 설명한다.
저장소는 두분으로 나뉘어진다. `$CVSROOT/CVSROOT'
는 CVS의 관리파일을 담고있다. 다른 디렉터리는 사용자가 정의한 모듈들을 담고 있다.
4-1. CVS에 저장소가 어디있는지 알리기
CVS에게 저장소가 어디있는지 알리는 몇가지 방법이 있다. 쉘명령어에서 -d (directory란 의미) 옵션으로 지정할수 있다.
그렇지 않으면 환경변수
cvs -d /usr/local/cvsroot checkout yoyodyne/tc
$CVSROOT
에 저장소의 절대위치를 지정할 수 있다. csh이나 tcsh인경우: sh이나 bash인 경우
setenv CVSROOT /usr/local/cvsroot
옵션 -d가 환경변수 $CVSROOT 보다 우선시된다.
CVSROOT=/usr/local/cvsroot
export CVSROOT
5. 로그인 하기
5-1. 로그인 하기
CVS서버가 설치된후 서버를 이용하려면 일단 로그인이 되어야 한다. 즉 다음장부터 나오는 작업을 하려면 먼저 로그인이 선행되어야 한다. 최초로 로그인이 성공하면 아이디와 패스워드가 저장되므로 두번다시는 암호와 패스워드를 입력하지 않다도 된다.
$ cvs -d :pserver:userid@cvs.hostname.com:/opt/cvsroot login
^^^^^^^ ^^^^^ ^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^
프로토콜 | 서버주소 혹은IP | 로그인명령
| |
사용아이디 저장소 디렉터리
중간의 -d옵션은 환경변수로 설정하면 생략가능하다.
$ CVSROOT=:pserver:userid@cvs.hostname.com:/opt/cvsroot
$ export CVSROOT
$ cvs login
로그인이 성공하면 `$HOME/.cvspass' 에 저장되어 다음부터 로그인 절차를 무시해도 된다
5-2. 유닉스 계정유저 - CVS만을 위한 유저
CVS에 사용되는 아이디는 두가지가 있다.
첫번째 방법은 유닉스의 어카운트를 그대로 cvs에사 사용하는 방법이다. 유닉스에 TELNET으로 들어갈 수있는 아이디가 있다면 CVS에서도 그대로 사용할 수 가 있다. 이 경우 사용자는 CVSROOT디렉토리에 사용권한을 가지고 있어야 한다.
두번째 방법은 CVS전용의 사용자를 만드는 방법이다. 이 방법은 시스템의 다른 부분의 보안에 영향을 주지 않기때문에 보다 안전하다. 그러나 유저의 생성과 삭제를 해주는 툴이 아직 만들어지지 않아서 번거롭다.
6. 저장소에 프로젝트 등록하기
저장소를 등록하려면 작업디렉토리가 있어야 하고, 저장소의 어느위치에 넣을지를 정해야 한다.
$ cd wdir
$ cvs import -m "Imported source" yoyodyne/rdir yoyo start
^^^^^^ ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^ ^^^^^
명령어 로그 옵션 프로젝트의
저장소 위치
wdir | CVS에 등록하려고 하는 파일들이 들어있는 폴더 |
-m "Imported source" | -m 옵션을 주지 않으면 CVS가 프롬프트로 물어보게 된다. |
yoyoydne/rdir | 저장소에서 위치가 $CVSROOT/yoyoydne/rdir 로 설정된다. |
yoyo | vendor 태그 - 판매구분 |
start | 릴리즈 태그 |
이렇게 하면 wdir의 내용이 $CVSROOT/yoyodyne/rdir 의 위치에서 관리된다.
이 작업이 완료되면 저장소의 내용과 원본과 같은지 확인을 하고, 원본 디렉토리를 지워야 한다.
처음 import한 직후는 checkout한 상태가 아니다. VSS와는 달리 별도로 checkout을 해야 작업디렉토리안의 파일과 저장소의 파일이 연관성을 갖는다. 원본(wdir)안의 내용은 백업한 후 지우는 것이 좋다. 같은 내용이 두곳에 존재하면 작업에 혼돈을 줄 수 있다. 이후부터 yoyodyne/rdir 에서 작업하고 저장소로 업데이트 하게된다.
$ cd ..
$ cvs checkout yoyoydne/rdir
$ diff -r wdir yoyoydne/rdir
$ rm -r wdir
checkout하는 과정은 작업파일과 저장소파일이 연관성을 만들어준다. 연관관계가 생긴 파일은 같은 디렉터리에 CVS란 디렉터리가 생성된다. 이 디랙터리안에 저장소와 연관되는 정보가 들어있다. 연관성을 없애려면 release 명령어를 이용한다.
release명령은 디렉터리와 저장소간의 연관관계를 없애준다. -d 옵션은 추가로 작업디렉토리도 지우라는 의미이다.
$ cvs release -d yoyoydne/rdir
7. CVS를 이용해 버전 관리
7-1. 저장소에서 파일 받아오기
이미 저장소에 프로젝트가 등록되었다면 CVS를 통해서 파일들을 받아올 수 있다. 방법은 6장에서 나와있는 방법과 동일하다.
이렇게 하면 현재의 디렉터리에 yoyodyne/rdir 이란 디렉터리가 생성되고 저장소로부터 파일들이 다운로드 된다.
$ cvs checkout yoyodyne/rdir
checkout은 위와같이 디렉터리단위로 할 수도 있지만, 파일단위로도 가능하다.
$ cvs checkout yoyodyne/rdir/somefile.txt
7-2. 수정된 파일을 저장소로 보내기
commit 명령어를 사용하면 된다. 디렉터리를 통체로 업데이트하려면
이렇게 하면, 지정한 디렉터리안의 수정된 파일들만 버전업되고 나머지파일은 업데이트 되지 않고 버전 번호를 그대로 유진한다.
$ cvs commit yoyodyne/rdir
이렇게 하면 파일 하나만 업데이트 할 수 있다.
$ cvs commit yoyodyne/rdir/somefile.txt
7-3. 구 리비젼으로 되돌리기
7-4. 저장소에서 파일 지우기
8. 리비젼 ( Revision )
많은 CVS사용에관해서 리비젼 번호를 너무 걱정할 필요가 없다; CVS가 1.1, 1.2 등등 으로 번호를 부여하고, 그것이 알아야할 모든 것이다. 어쨋거나, 어떤 사람들은 CVS가 리비젼번호를 어떻게 부과하는지에 관한 지식과 제어를 좀더 알고싶어한다.
하나이상의 파일을 포함한 리비젼들을 추적하고 싶다면, 태그(tag)를 사용하라. 태그는 각 파일의 숫자 리비젼을 부과하는 심볼 리비젼이다. <※주: 복잡한 말인데 뒤를 읽어보면 더 자세히 설명이 나온다>
8-1. 리비젼 번호
각각의 버전의 파일은 1.1, 1.2, 1.3.2.2, 혹은 1.3.2.2.4.5와 같이 고유한 리비전번호(revision number)를 갖는다. 리비전번호는 항상 마침표로 구분된 짝수의 숫자로 이루어져 있다. 1.1이 기본 값으로 파일의 첫번째 리비젼 값이 된다. 각각의 연속된 리비젼은 가장 오른쪽 숫자를 하나 증가해서 새로운 값으로 부여된다. 다음의 그림은 오른쪽이 최신 리비젼인 그림이다.
+-----+ +-----+ +-----+ +-----+ +-----+1.3.2.2 와 같이 하나이상의 마침표를 포함하는 숫자일 수 있다. 이러한 리비젼은 가지(branch)의 리비젼을 표현한다.
! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 !
+-----+ +-----+ +-----+ +-----+ +-----+
8-2. 버젼(Versions), 리비젼(Revisions), 배포(Releases)
위에서 설명한대로 파일은 여러개의 버전을 가질 수 있다. 게다가 소프트웨어 제품도 종종 4.1.1과 같은 버전번호가 부과된다.
첫번째 예에서 버전은 이 문서에서 리비젼이라고 불리우고, 또 다른 예에서는 배포(release)라고 불리운다. 혼동을 피하기위해서 이 문서에서 버젼이란 말은 사용하지 않는다.
8-2. 리비젼 부과
지정하지 않으면 CVS는 첫번째 숫자는 바꾸지 않고, 두번째 숫자를 증가하는 숫자 리비젼을 부여할 것이다. 예를 들면 1.1, 1.2, 1.3 등등
새로운 파일을 추가할 때, 두번째 숫자는 언제나 1이고 첫번째 숫자는 그 디렉토리에 있는 파일중 가장 높은 첫번째 숫자와 같을 것이다. 예를 들면, 현재의 디렉터리중에 있는 파일중 가장높은 리비젼이 1.7, 3.1, 4.12 라면 추가되는 파일은 4.1 란 리비젼을 가질 것이다.
보통은 리비젼번호에 관심을 가질 이유가 없다. 리비젼들을 CVS가 관리하는 내부적인 것으로 여기는 것이 편하고, 태그(tag)가 배포판1로부터 배포판2와 같은 것들을 구분하는데 더 좋은 방법을 제공한다. 어쨋든, 리비젼번호를 설정하고 싶으면 cvs commit
할 때 `-r
'옵션을 부과하면 된다. -r
옵션은 -f
옵션을 포함하는 것으로 여겨진다. 즉 파일이 수정되지 않았더라도 강제적으로 커밋시켜버린다.
예를 들면, 모든 파일들을 리비젼3.0으로 올리기 위해서는 다음을 호출하면 된다.
주의할 것은 -r옵션으로 자정하는 숫자가 반드시 존재하는 리비젼보다 커야한다는 점이다. 만약 3.0리비젼이 존재한다면 `
$cvs commit -r 3.0
cvs commit -r 1.3
아로 할 수 없다. 만약 여러개의 배포판을 동시에 관리하길 원한다면 가지치기(branch)를 사용하라.
8-3. 태그 (tag)
리비젼 번호는 독립적으로 진핸된다. 리비젼 번호는 소프트웨어 제품의 배포번호와는 아무런 연관성을 가질 필요가 없다. CVS를 어떻게 사용할지에 따라서 리비젼번호는 두 배포판간에 여러번 바뀔 수 있다. 예를 들면, RCS 5.6으로 작성된 몇몇 소스파일들이 다음과 같은 리비젼 번호를 갖는다고 하자.
ci.c 5.21
co.c 5.9
ident.c 5.3
rcs.c 5.12
rcsbase.h 5.11
rcsdiff.c 5.10
rcsedit.c 5.11
rcsfcmp.c 5.9
rcsgen.c 5.10
rcslex.c 5.11
rcsmap.c 5.2
rcsutil.c 5.10
tag
명령어를 이용해서 심볼 이름을 어떤 파일의 리비젼번호에 줄 수 있다. status
명령어에 -v
옵션을 줘서 파일이 가지고 있는 모든 태그들을 볼 수 있다. 태그명은 반드시 알파벳 대소문자로 시작해야 해야하고, 알파벳 대소문자, 숫자, '-', '_'로 이루어져있다. 두 태그명 BASE
와 HEAD
는 CVS가 특별한 용도로 정의되어있어서 사용할 수 없다. CVS에서 특별한 용도로 사용될 이름들은 실제의 태그명들과 충돌을 피하기위해서 이름붙여질 것이다.
프로그램의 이름과 배포판의 버젼번호같은 정보를 기본으로 한 규칙에 의해서 태그의 이름을 붙이지를 원할것이다. 예를 들면 프로그램의 이름을 사용하고 뒤에 버전번호를 `.'를 `-'로 바꿔서 붙이면 CVS 1.9는 cvs1-9
라고 태그를 붙일 수 있다. 모순없는 규칙을 정한다면 태그가 cvs-1-9
인지 cvs1-9
인지 빈번히 추측하지 않을 것이다. 또한 태그정보파일(taginfo file)안의 고딩규칙을 강제화하는 것을 고려할수도 있다.
다음 예는 파일에 태그를 어떻게 붙이는지 보여준다. 명령어는 반드시 작업디렉터리안에서 실행되어야 한다. 다시말하면 `backend.c'
파일이 있는 디렉터리에서 명령을 실행시켜야 한다는 것을 의미한다.
(디렉터리를 CVS에게 인수로 준다면, 해당하는 처리를 디렉터리안의 모든 파일과 그 서브디렉터리안의 파일에까지 재귀적으로 적용한다는 것을 의미한다.
$ cvs tag rel-0-4 backend.c
T backend.c
$ cvs status -v backend.c
===================================================================
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: (none)
Sticky Date: (none)
Sticky Options: (none)
Existing Tags:
rel-0-4 (revision: 1.4)
checkout
명령어는 `-r'
명령을 가지고 있는데, 이 옵션은 모듈의 임의의 리비젼을 채크아웃하게 해준다. 이 옵션은 차후에 `tc'란 모듈의 1.0배포판이라고 붙여진 소스들을 얻어오는 것을 수월하게 해준다.
이것은 아주 유용하다. 예를 들면, 누군가 그 배포판에 버그가 있다고 요청하고 현재의 작업카피에서는 버그를 찾을수 없다.
$ cvs checkout -t rel-1-0 tc
주어진 날자의 상태대로 모듈을 채크아웃할 수 있다. `-r'옵션을 이런 명령어에 준다면, 접착성 태그에 관해서 고려할 필요를 느낄것이다. (?)
한개이상의 파일에 동일한 태그를 붙였다면 태그를 "파일명 리비젼번호로 구성된 행열에 그은 곡선"으로 생각될수 있다. 다음과 같은 리비젼을 가진 5개의 파일이 있다고 하자. (*주: 아래그림에서는 곡선으로 안보이지만 곡선으로 생각하자)
과거의 어떤 시점에 *버전에 태그가 붙여졌다. 태그를 태그붙여진 리비젼들을 잇는 곡선의 손잡이라고 여길 수 있다. 이 손잡이를 잡아당기면 모든 태그붙여진 리비젼들을 얻을 수 있다. 이것을 보는 다른 방법은 다음과 같이 태그가 붙여지 리비젼들을 평평하게 놓고 보는 것이다.
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
file1 file2 file3 file4 file5
1.1
1.2
1.1 1.3 _
1.1 1.2 1.4 1.1 /
1.2*----1.3*----1.5*----1.2*----1.1 (--- <--- Look here
1.3 1.6 1.3 \_
1.4 1.4
1.5
8-4. 접착성 태그 (Sticky Tags)
때때로 작업중인 카피본의 리비젼은 리비젼과 관련된 테이터를 가지고 있다. 예를 들면 리비젼은 가지(branch)이거나, `checkout -D'
나 `update -D'
에의해서 어떤 날자에 우선해 버젼으로 제한될 수 있다. (뭔말이여?) 왜냐하면, 이 날자는 접착된것(Sticky)으로 여겨진다.
대부분의 경우에, 접착성(stickiness)은 생각할 필요가 없는 CVS의 불투명한 면이다. 어쨋거나, 이 기능을 사용하지 않았으면 하더라도 접착성 태그(sticky tahg)에 관해서 좀 알필요가 있다. (예를 들면 어떻게 이것을 안쓰는지에 관해서)
어떤 접착성 태그들이나 날자가 설정되었는지 보기위해서 status
명령어를 쓸 수 있다.
이 접착성 태그들은
$ cvs status driver.c
===================================================================
File: driver.c Status: Up-to-date
Version: 1.7.2.1 Sat Dec 5 19:35:03 1992
RCS Version: 1.7.2.1 /u/cvsroot/yoyodyne/tc/driver.c,v
Sticky Tag: rel-1-0-patches (branch: 1.7.2)
Sticky Date: (none)
Sticky Options: (none)
`cvs update -A'
명령어로 지울때까지 당신의 작업파일들에만 남아있다. `-A'옵션은 본체의 해더로부터 파일의 버젼을 받아오고, 다른 접착성태그들과 날자들과 다른 옵션들은 무시한다.
가장 일반적인 접착성태그의 사용은 어떤 가지(branch)가 작업진행중인지를 구분하는 것이다. 어쨋건, 가지쳐지지 않은 접착성태그들도 또한 사용법을 가지고 있다. 예를 들면, 다른 사람들이 만드는 불안정한 수정내용들로부터 현재 작업중인 디렉터리가 업데이트되는 것을 막고싶다고 가정하자. 물론 `cvs update'
를 실행하는 것을 자제할수 있다. 그러나 거대한 트리의 일부분만을 업데이트하는 것을 막고싶다면 접착성 테그가 그것을 도와줄 수 있다. 만약 어떤 리비젼으로 채크아웃한다면 (예를 들어 리비젼 1.4) 이 리비젼은 접착성 태그가 된다. 그 뒤에 이어서 cvs update
명령어는 당신이 cvs update -A
명령어를 해서 리셋하기전까지는 최신 리비젼을 받아오지 않을것이다. 마찬가지로, update
나 checkout
명령에 `-D'옵션의 사용은 적착성 날자(sticky date) 를 설정한다. 이 방법은 나중에 파일을 받아올때 그 날자가 사용되게 해준다.
사람들은 보통 접착성태그의 사용없이 구버전의 파일을 받아오길 원한다. 이것은 checkout
나 update
에 `-p'
옵션을 줘서 할 수 있다. 예를 들어서
어쨌든, 이미 채크인한 것을 되돌릴때 이것이 가장 간단한 방법이다.
$ cvs update -p -r 1.1 file1 >file1
===================================================================
Checking out file1
RCS: /tmp/cvs-sanity/cvsroot/first-dir/Attic/file1,v
VERS: 1.1
***************
$
9. 가지나누기와 병합 ( Branching and merging )
CVS에서는 가지나누기(branch)라고 알려진 방법을 통해서 독립된 개발선상에서 작업할수 있다. 가지에 있는 파일을 바꿔도 본작업이나 다른 가지의 작업에는 영향을 주지 않는다.
후에 당신은 병합(merging)을 통해서 한쪽 가지에 한 수정을 다른쪽 가지로 옮길 수 있다. 병합은 수정내용을 작업디텍터리에 넣는 `cvs update -j'
를 포함한다. 그런후 그 리비젼을 거밋할 수 있고, 그렇게 해서 작업을 다른 가지에 수정내용을 효과적으로 옮길 수 있다.
9-1. 가지나누기의 잇점
tc란 모듈의 릴리즈 1.0이 만들어졌다고 가정하자. 당신은 tc를 계속 개발하면서 릴리즈 1.1을 두달동안 만들기로 계획을 세웠다. 그러는 중에 고객이 치명적인 버그에 대해서 불평을 하기 시작하였다. 당신은 릴리즈1.0을 채크아웃하고 버그를 찾는다. 어쨋거나 소스의 현재 리비젼은 고쳐지고 있는 상태이고 적어도 한달쯤 지나야 안정화될 것이다. 새로운 소스를 기반으로 버그를 고칠수 있는 방법이 없다.
이런 경우에 하는 것이 리비젼 트리에 가지를 생성하는 것이다. (이 가지에는 모든 파일이 tc의 릴리즈1.0이라고 적혀있다.) 당신은 본체에 방해를 주지않고 수정을 만들 수 있다. 수정이 끝났을 때 수정내용을 본체에 혼합하던가 가지에 그대로 남겨두던가 결정할 수 있다.
9-2. 가지 생성하기
현재 디렉터리가 작업폴더라고 가정하면 tag -b
로 가지를 생성할 수 있다.
이것은
$ cvs tag -b rel-1-0-patches
`rel-1-0-patches'
란 이름을 부여함으로서 작업본에 있는 현재 리비젼들을 기반으로 가지를 분리한다. 가지가 작업본에 생성되는 것이 아니라 저장소에 생성된다는 것을 이해하는 것이 중요하다. 예와 같이 현재의 리비젼을 기반으로 가지를 생성하는 것은 작업본을 새로운가지로 자동으로 전환하지 않는다.
또한 rtag
를 통해 어떠한 작업본의 참조 없이도 가지를 만들 수 있다.
$ cvs rtag -b -r rel-1-0 rel-1-0-patches tc
`-r rel-1-0'
는 이 가지가, 태그`rel-l-0'
에 해당하는 리비젼의 루트가되어야한다는 것을 나타낸다. `rel-1-0'
가 가장 최신의 리비젼일 필요는 없다. -- 종종 과거리비젼에서 가지치는 거이 유용하다. (예를들어, 과거 릴리즈에서 버그를 잡는 것이 안정하다고 알려져있다.)
`tag'
를 사용하면서 `-b'
옵션은 rtag가 (단지 심볼릭 리비젼 명을 만드는 것이 아니라) 가지를 만든다는 것을 나타낸다. `rel-1-0'
와 일치하는 파일들의 숫자 리비젼은 파일마다 틀릴것이다.
그렇다면 이 명령어의 전체 결과는 모듈 `tc'안에 (`rel-1-0-patches'
라고 이름 붙여진) 새로운 가지를 생성하는 것인데, 이 가지는 `rel-1-0'
라고 지정된 리비젼트리의 루트이다. --
9-3. 가지 제어하기
$ 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)
9-4. 가지와 리비젼
+-----+ +-----+ +-----+ +-----+ +-----+
! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 !
+-----+ +-----+ +-----+ +-----+ +-----+
+-------------+
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 !
+---------+ +---------+ +---------+
9-5. 마법 가지 번호 (Magic branch numbers)
$ cvs admin -NR4patches:1.4.2 numbers.c
9-6. 가지 전체를 병합하기
+-----+ +-----+ +-----+ +-----+
! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 ! <- The main trunk
+-----+ +-----+ +-----+ +-----+
!
!
! +---------+ +---------+
Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 !
+---------+ +---------+
$ cvs checkout mod # Retrieve the latest revision, 1.4
$ cvs update -j R1fix m.c # Merge all changes made on the branch,
# i.e. the changes between revision 1.2
# and 1.2.2.2, into your working copy
# of the file.
$ cvs commit -m "Included R1fix" # Create revision 1.5.
$ cvs checkout -j R1fix mod
$ cvs commit -m "Included R1fix"
9-7. 하나의 가지에서 여러번 병합하기
9-8. 두 리비젼간의 다른점 병합하기 (Merging difference between any two revisions) --> ?
+-----+ +-----+ +-----+ +-----+ +-----+
! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk
+-----+ +-----+ +-----+ +-----+ +-----+
! *
! *
! +---------+ +---------+
Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 !
+---------+ +---------+
+-----+ +-----+ +-----+ +-----+ +-----+
! 1.1 !----! 1.2 !----! 1.3 !----! 1.4 !----! 1.5 ! <- The main trunk
+-----+ +-----+ +-----+ +-----+ +-----+
! *
! *
! +---------+ +---------+ +---------+
Branch R1fix -> +---! 1.2.2.1 !----! 1.2.2.2 !----! 1.2.2.3 !
+---------+ +---------+ +---------+
cvs update -j 1.2.2.2 -j R1fix m.c # Merge changes from 1.2.2.2 to the
# head of the R1fix branch
cvs update -j R1fix:yesterday -j R1fix m.c
cvs update -j merged_from_R1fix_to_trunk -j R1fix m.c
9-9. 병합은 파일을 추가하거나 삭제할 수 있다.
$ cvs update -j 1.5 -j 1.3 backend.c
$ 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
$
9-10. 병합과 키워드
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
10. 공동작업
여러명이 공동으로 작업하는 경우에 두명이상의 사람이 한파일을 동시에 checkout 할 수가 있다. 첫번째 해결책은 파일잠금(file locking)혹은 예약된 채크아웃(Reserved checkout)으로 알려진 방법이다. 이 방법은 한번에 한사람만 파일을 수정할수 있게 하는 방법이다. 이 방법은 RCS나 SCCS등에서 제공하는 유일한 방법이다. CVS에서 예약된 채크아웃을 하는 일반적인 방법은 cvs admin -l 명령이다. 이 방법은 CVS와 훌륭하게 통합되어있지 않다. 필요한 사람만 사용하도록 하자.
CVS에서 기본으로 사용하는 방식은 예약안한 채크아웃 (unreserved checkout) 이다. 이 방식에서 여러개발자가 동시에 한 파일을 자신들의 작업디렉터리에서 작업할 수 있다. 수정내용을 커밋하는 첫번째 개발자는 다른 사람이 그것을 수정하기 시작했다는 것을 자동적으로 알수있는 방법이 없다. 다른 사람들은 그 파일을 커밋하려고 할때 에러메시지를 받는다. 그들은 자신들의 작업용 카피가 업데이트되도록 CVS명령어를 사용해야만 한다. 이 공정은 거의 자동이다.
10-1. 파일 상태
채크아웃된 파일에 어떤 처리를 했느냐에 따라, 그리고 다른이들이 저장소에있는 파일들에 어떤 처리를 했느냐에 따라, 한 파일은 여러 상태로 분류될수 있다. 상태들은 status 명령으로 볼 수 있다.
Up-to-date | 파일이 사용되고 있는 가지(branch)의 저장소의 최신 리비전과 동일하다. |
Locally Modified | 당신이 파일을 수정하였고, 아직 커밋하지 않아 저장소에는 반영되지 않았다.. |
Locally Added | 당신이 add명령으로 파일을 추가하였지만, 아직 커밋하지 않아 저장소에는 반영되지 않았다. |
Locally Removed | 당신이 remove명령으로 파일을 삭제하였지만 커밋하지 않아 저장소에는 반영되지 않았다. |
Needs Checkout | 다른 사람이 이미 새로운 리비전으로 커밋해버렸다. 이 이름은 약간 혼란을 준다. 보통 commit 가 아니라 update 로 새로운 리비전을 받아온다 |
Needs Patch | Need Checkout와 같지만, CVS서버가 전체파일을 보내주기보다 패치를 보내줄것이다. 전체 파일을 보내주거나 패치를 보내주거나 결과물은 같다. |
Needs Merge | 누군가가 저장소에 새로운 리비전을 커밋했고, 당신도 그 파일을 수정했다. |
File had conflicts on merge | 선행된 update 명령이 충돌(conflict) 되었다는 것을 빼고는 Locally Modified와 같다. 뒤에 설명하는 충돌 해결을 참고하세요. |
Unknown | CVS가 이 파일에 관해서 아무것도 모른다. 예를 들면 새로운 파일을 만들고 add 를 하지 않았다. |
status
명령어와 update
명령어를 다소 보충적인 것으로 생각할 수 있다. 파일을 업데이트하기위해 update
명령을 사용하고, update
가 무었을 했었나를 보기위해 status
를 사용할 수 있다. 사실 status
명령보다 간략한 포멧으로 상태를 보고싶다면, 다음을 쓸수도 있다.
$ cvs -n -q update
10-2. 파일 업데이트
파일을 업데이트하거나 병합(merge)하고 싶을때는 update 명령어를 사용하라. 이것은 갱신되지않은 파일인 경우는 commit명령과 대충 동등하다 : 최신 리비젼의 파일이 저장소로부터 추출되어 작업디렉토리에 놓여진다.
파일의 수정내용은 update를 사용할때 없어지지 않는다. 더 새로운 리비젼이 존해하지 않는다면 update는 아무것도 안한다. 파일을 수정했고 새로운 리비젼이 있다면, CVS는 모든 수정내용을 작업디렉터리에 병합한다.
10-3. 충돌의 예
리비젼 1.4인 파일 driver.c가 있다고 가정하자.
#include리비전 1.6인 driver.c가 다름과 같다고 가정하자.
void main()
{
parse();
if (nerr == 0)
gencode();
else
fprintf(stderr, "No code generated.\n");
exit(nerr == 0 ? 0 : 1);
}
#include이때 cvs update 를 실행하면 다음과 같은 결과를 얻는다.
int main(int argc,
char **argv)
{
parse();
if (argc != 1)
{
fprintf(stderr, "tc: No args expected.\n");
exit(1);
}
if (nerr == 0)
gencode();
else
fprintf(stderr, "No code generated.\n");
exit(!!nerr);
}
CVS가 충돌이 났다는 메시지를 보낸다. 당신이 작업하던 원본파일은 수정한된채로 `.#driver.c.1.4'란 이름으로 저장된다. 새로운 버전의 driver.c에는 다음의 내용이 저장된다.
$ cvs update driver.c
RCS file: /usr/local/cvsroot/yoyodyne/tc/driver.c,v
retrieving revision 1.4
retrieving revision 1.6
Merging differences between 1.4 and 1.6 into driver.c
rcsmerge warning: overlaps during merge
cvs update: conflicts found in driver.c
C driver.c
#include덮어씌울 수 없는 수정된부분이 작업본에 합체되고, 겹쳐진 부분은 `<<<<<<<', `=======' and `>>>>>>>' 로 구분되는 것에 주목해라.
#include
int main(int argc,
char **argv)
{
init_scanner();
parse();
if (argc != 1)
{
fprintf(stderr, "tc: No args expected.\n");
exit(1);
}
if (nerr == 0)
gencode();
else
fprintf(stderr, "No code generated.\n");
<<<<<<< driver.c
exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
=======
exit(!!nerr);
>>>>>>> 1.6
}
마커를 지우고 잘못된 라인을 수정하는 것으로 문제를 해결한다. 다음과 같이 수정했다고 가정하자
#include그러면 계속할 수 있고, 이 것을 리비젼1.7로 커밋할 수 있다.
#include
int main(int argc,
char **argv)
{
init_scanner();
parse();
if (argc != 1)
{
fprintf(stderr, "tc: No args expected.\n");
exit(1);
}
if (nerr == 0)
gencode();
else
fprintf(stderr, "No code generated.\n");
exit(nerr == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
$ cvs commit -m "Initialize scanner. Use symbolic exit values." driver.c
Checking in driver.c;
/usr/local/cvsroot/yoyodyne/tc/driver.c,v <-- driver.c
new revision: 1.7; previous revision: 1.6
done
출처 : http://user.chollian.net/~akalpa/cvs/cvs.html