Contents

그동안 형상관리를 위해서 CVS를 주로 써왔는데, 여차저차해서 SVN을 사용하는 경우가 많아졌으니 틈틈히 정리해볼 생각이다.

소개

형상관리에 대해서

소프트웨어 형상 관리 (Sofware Configuration Management)는 소프트웨어의 효율적인 개발/운용/유지/보수를 위한 포괄적인 학술분야 영역이다. 형상관리의 대상은 다음과 같은 것들이 있다.
  1. SCM : Source code management
SVN, CVS, VC 등의 툴이 여기에 포함된다.
  1. CMS : change management
Bugzilla, JIRA, TRAC 등의 툴이 여기에 포함된다.
  1. BM : Build management
ant, hudson

software 버전관리에 대해서

여러명이 함께 소프트웨어를 개발해야 하는 경우, 게다가 물리적으로 멀리 떨어져서 작업을 해야 하는 경우에 소프트웨어 버전관리가 중요한 이슈가 된다. 가능한 한명의 개발자가 하나의 소스코드를 책임질 수 있도록 모듈화 컴포넌트화 하도록 설계하긴 하지만, 분명한 한계가 있다. 여기에 코드에 문제가 생겼을 때, 이전버전을 찾아서 복구해야 하는 문제, 실수로 소스코드를 덮어써버리는 문제, 하나의 코드를 두명 이상이 작업할때의 소스코드 충돌등의 문제가 발생하게 된다. 수십명의 개발자가 개발에 참여할 경우 혹은 인터넷을 통하여 공동으로 개발에 참여할 경우라면 이러한 문제를 해결할 수 있는 시스템을 반드시 구축해야 한다.

소프트웨어 버전관리는 위에서와 같은 상황 - 여러명이 공동작업을 해야 하는 경우, 혹은 물리적으로 멀리 떨어진 개발환경 -에 있어서 소프트웨어 코드가 하나의 개발흐름을 이어나갈 수 있도록 만들어준다.
  1. 소스코드의 수정시 기록을 남긴다. 후에 소스코드를 원상복구 할 수 있도록 해준다.
  2. 충돌을 방지하고 해결한다.
  3. 변경사항을 추적할 수 있도록 한다.
  4. (완전하진 않지만)백업환경을 만들 수 있다.
일반적으로 소프트웨어 버전관리는 두명이상이 작업하는 경우에 특히 유용하지만 개인이 사용할 때도 버전관리 프로그램을 사용하는게 좋다. 버전관리 프로그램의 사용없이 개발하다가 코드삭제,덮어쓰기와 같은 일을 당해서 몇시간 혹은 몇일을 고생해본 경험이 있다면 이 말을 이해할 것이다.

SVN에 대해서

SVN은 형상관리대상 요소들 중 소프트웨어 버전관리를 위해서 사용되는 SCM 툴이다. SVN에 사용되기전에는 CVS라는 툴이 오랫동안 돼 왔다. SVN은 기본적으로 CVS와 비슷한 버전관리 체계를 가지고 있다. 애초에 CVS를 개선시키고자 하는 목적에서 만들어졌기 때문이기도 해서, 명령어들까지 비슷하다. 때문에 어렵지 않게 SVN으로 넘어올 수 있다. 물론 최근에 개발된 툴이니 만큼 CVS보다는 좀더 진보된 기능들을 가지고 있으며, CVS에서 SVN으로 넘어오는 추세다.

다음은 SVN이 가지고 있는 장점들이다.
  1. plain text 코드 뿐만 아니라 바이너리 데이터도 지원한다
  2. 커밋의 단위가 개별파일이 아닌 작업단위다.
  3. cvs에 비해서 빠르다.
  4. 파일 이름변경, 이동, [wiki:디렉토리에 대한 버전관리
  5. atomic 커밋. cvs는 파일단위로 commit를 한다. 그래서 여러개의 파일을 commit 하는중에 몇개가 실패할경우 성공된것만 적용되고 실패한것은 그대로 남게 된다. svn은 작업단위이기 때문에 commit에 실패하는게 하나라도 있으면 commit이 되지 않는다. 소프트웨어는 파일단위가 아닌 작업단위로 움직이는 것으로 봐야하므로 svn의 작업단위 commit 정책이 더 합리적이라 할 수 있다.

SVN 아키텍처

http://tntbase.org/export/429/branches/nodestorage/SVN/src-trunk/www/subversion-diagram.png

SVN은 Repository와 Repository에 접근하기 위한 Interface만을 제공하는 간단한 시스템이다.

SVN 설치와 기본 사용

설치

이 문서는 Ubuntu Linux 운영체제를 대상으로 만들어졌다. 윈도우 화경은 고려하지 않았다.

apt-get을 이용해서 간단하게 설치할 수 있다.
# sudo apt-get install subversion
Ubuntu가 아닌 rpm 기반의 리눅스도 각 배포판에서 제공하는 패키지 관리 툴로 간단하게 설치할 수 있을 것이다. 직접 소스를 컴파일해서 설치하는 방법등은 설명하지 않을 생각이다..

Repository

SVN을 다루기 전에 Repository의 개념에 대해서 확인하고 넘어가는게 좋을 것같다.

Repository는 코드의 원본이 저장되는 공동저장공간이다. 코드를 사용하는 개발자들은 이 저장공간 - 이하 Repository -에 있는 자원인 코드를 공유한다. 공유는 Repository에 프로젝트를 만들고 이 프로젝트의 파일들을 읽고, 쓰는 방식으로 이루어진다. SVN은 Repository를 준비하고, Repository에 읽고 쓰는 작업을 할때, 코드가 충돌이 나는지를 확인하며 업데이트 내용을 기록함으로써 버전정보를 만들고 충돌을 복구하며, 필요할때 코드를 원복시키는 등의 일을 한다.

SVN 서버와 SVN 클라이언트

SVN은 서버/클라이언트 모델을 따르는 소프트웨어다. SVN 서버는 Repository를 관리를 하고, SVN 클라이언트는 SVN 서버에게 코드를 읽고 쓰는 등의 요청을 한다.

SVN Repository 접근 방식

SVN 은 다양한 방식으로 Repository에 접근할 수 있도록 하고 있다.
file:/// 로컬디스크로의 접근
http:// 웹으로의 접근
https:// SSL(:12)을 통한 웹으로의 접근
svn:// SVN 서버 프로토콜을 이용한 접근
svn+ssh:// ssh(:12)터널을 통한 svn 서버를 이용한 접근

SVN 서버 설정

SVN서버 설정은 크게 아래의 3단계 과정을 거친다.
  1. 공유할 파일이 존재하는 Repository 설정
  2. SVN Repository 의 접근 방식설정
  3. 계정설정

Repository 생성

svnadmin 을 이용해서 Repository를 만들 수 있다. repository는 일반파일시스템berkeley DB타입이 있다. 특별한 옵션을 사용하지 않으면 berkeley db 형식으로 생성되며 --fs-type를 이용해서 타입을 변경할 수 있다. repository 의 위치는 /home/project 로 하겠다.

먼저 /home/project 디렉토리를 생성한다.
# mkdir /home/project

이제 저장소를 생성한다. 이름은 sample로 해다. 보통 저장소는 프로젝트와 대응하며 프로젝트의 특성을 잘 표현하는 이름을 가지도록 한다.
# 일반 파일시스템으로 생성
$ svnadmin create --fs-type fsfs /home/project/sample
# berkeley db 형식으로 생성
$ svnadmin create --fs-type bdb /home/project/sample
일반파일시스템 대신에 굳이 bdb를 써야할 분명한 이유가 있는지는 확실하지 않다. berkeley db는 NFS, AFS, SMB 등으로 공유하고자 할때, 문제가 생길 수 있으니 그냥 일반파일시스템으로 생성하도록 하자.

SVN Repository 접근방식 설정

여러가지 방법이 있는데, 여기에서는 svn 프로토콜을 이용한 접근방식에 대해서 알아보도록 하겠다.

svn은 svnserve 라는 가벼운 서버프로그램을 제공하며, 이것을 이용해서 서버를 실행시킬 수 있다. 이렇게 해서 서버를 실행하면 svn:// 혹은 svn+ssh:// URL 형식을 이용해서 서버에 접근할 수 있다. /etc/service 를 열어보면 svn 서비스 포트가 정의되어 있는 것을 확인할 수 있다. 표준 서비스 포트는 3690을 사용한다..
svn             3690 / tcp(:12)        subversion      # Subversion protocol
svn             3690 / udp(:12)        subversion

svnserv는 stand alone 모드, 즉 daemon(:12)모드와 inetd(:12)모드 둘 중 선택해서 띄울 수 있다. 여기에서는 데몬모드로 실행시키는 방법에 대해서만 알아볼 것이다.

먼저 접근을 허용할 계정을 만들자 계정관련 설정파일은 /home/project/sample/conf 밑에 있다. svnserve.conf 에서 인증에 사용될 방법을 설정할 수 있는데, general 섹션의 password-db 를 다음과 같이 수정하자.
[general]
# passwd 파일로 패스워드를 관리한다.
password-db = passwd
이제 /home/project/conf/passwd 파일에 아래와 같은 형식으로 유저계정정보를 입력하면 된다.
#username = password
yundream = 123456

svn 서버를 실행한다. -d는 데몬모드로 실행하기 위해서, -r은 repository를 지정하기 위한 옵션이다.
$ svnserve -d -r /home/project
# 3690 포트가 열려있는 것을 확인할 수 있을 것이다. netstat(:1)로 확인해보자. 
$ netstat -na | grep 3690
tcp        0      0 0.0.0.0:3690            0.0.0.0:*               LISTEN

이제 trunk, branches, tags 디렉토리를 생성한다.
$ svn mkdir svn://localhost/sample/trunk --username yundream
$ svn mkdir svn://localhost/sample/branches --username yundream
$ svn mkdir svn://localhost/sample/tags --username yundream

프로젝트를 생성하자

이렇게 해서 sample 프로젝트를 수행할 저장소까지 갖추어졌다. 이제 프로젝트진행을 위해서 필요한 코드를 올리면 되는데, 이작업을 import라고 한다.

일단 myproject 라는 디렉토리를 생성한다. 이 디렉토리에는 hello.c 라는 코드를 가지고 있다.
# mkdir myproject
다음은 hello.c 의 내용이다.
int main(int argc, char **argv)
{
	printf("Hello World!!!\n");
}

import 옵션을 이용해서 프로젝트를 생성한다.
# svn import myproject svn://localhost/sample/trunk --username yundream
인증 영역(realm): <svn://localhost:3690> 51c49533-dcd9-41dc-b726-0c7fb9b7a5fc
'yundream'의 암호:
추가          myproject/hello.c
lib파일들을 추가해야 할 경우 --no-ignore --force 옵션을 사용하자

svn 클라이언트 설정

svn 패키지를 설치하면 클라이언트까지 설치가 되므로 딱히 신경쓸 것은 없다. 다만 에디터 환경정도는 신경을 쓸 필요가 있다.

svn은 프로젝트를 import 하거나 commit 혹은 update 할때, 간단한 메시지를 남길 수 있다. 이 메시지는 로그형태로 관리가 되어서, 나중에 이슈를 추적하는데 도움이 된다. 이 메시지를 편집할때 어떤 에디터를 사용할지를 지정할 수 있다.

사용할 에디터는 환경변수(:12) SVN_EDITOR 에 에디터를 지정하면된다. vi(:12)를 사용하고 싶다면 아래와 같이 하면된다.
# export SVN_EDITOR=/usr/bin/vi

로그인할때 자동으로 환경변수가 지정되도록 하고 싶다면 홈디렉토리의 .bash_profile 에 위의 명령을 추가하면 된다.

프로젝트를 받아오자

repository를 생성하고 프로젝트까지 등록시켰으니 함께 프로젝트를 진행할 기본적인 환경은 갖추어졌다고 볼 수 있다. 이제 프로젝트 참가자는 프로젝트를 받아와서 원하는 작업을 하면 된다.

최초에 프로젝트를 받아오는 작업을 checkout라고 한며, co 혹은 checkout 옵션을 이용해서 프로젝트를 받아올 수 있다.

sample 프로젝트를 myproject 디렉토리로 받아와 보자.
# svn co svn://localhost/sample/trunk --username yundream myproject
A   myproject/hello.c

작업한 내용을 올리자

프로젝트를 checkout을 했으니 소스코드를 수정하도록 하자. hello.c 프로그램은 별 문제없이 compile(:12)되고 실행되긴 하지만, main 함수가 return 값을 가지고 있지 않다는 것과 printf(:3)를 위한 stdio.h 헤더파일을 인클루드 시키지 않고 있다는 사소한 문제점을 가지고 있다. 그래서 아래와 같이 소스코드를 수정했다.
#include <stdio.h>

int main()
{
	printf("Hello World!!!\n");
	return 0;
}
작업을 했으니, 프로젝트에 참여한 다른 개발자들도 확인할 수 있도록 코드를 올려야 할 것이다. 이것을 commit이라고 한다.
# svn commit --username yundream
인증 영역(realm): <svn://localhost:3690> 51c49533-dcd9-41dc-b726-0c7fb9b7a5fc
'yundream'의 암호:
전송중         hello.c

저장소의 내용을 받아오자

아침에 컴퓨터를 키고 프로젝트를 수행하기로 했다면, 가장 먼저해야 할일은 어제 새로 바뀐내용이 있는지를 확인해서 최신의 소스코드를 받아오는 일이다. 프로젝트를 동기화 하는 것이라고 보면 된다. upate 혹은 up옵션을 이용해서 레포지토리로 부터 최신 소스코드를 가져올 수 있다.

$ svn up hello.c
U    hello.c
업데이트 된 리비전 4.

새로운 파일 추가하기

작업을 하다보면 새로운 코드라든지 문서등을 추가시켜야 하는 경우가 생길 수 있다. sample 프로젝트를 설명한 README.txt 파일을 새로 만들었다고 가정해보자. 새로 생성한 파일은 저장소에 올려서 버전관리 대상으로 하고 싶다면 add 옵션을 이용해서 추가할 것을 명시하면 된다.
$ svn add README.txt
A  (bin)  README.txt
추가했다고 해서 즉시 저장소에 올라가는 건 아니다. commit 를 해야 비로서 저장소에 올라간다.
$ svn commit -m "README 파일 추가"
추가    (bin)  README.txt
파일 데이터 전송중 .
커밋된 리비전 5.
이제 다른 개발자들이 update 하는 시점에서 다른 수정된 내용들과 함께 README.txt 파일을 받아오게 될 것이다.

SVN 운용

trunk, branches, tags의 차이

trunk, branches, tags의 뜻을 알아야 제대로 활용할 수 있겠죠.
  • trunk : 주요 개발 줄기입니다. 프로젝트는 trunk에서 시작이 됩니다.
  • branches : 어떤 이유로 trunk에서 복사돼서 새로 만들어진 프로젝트입니다. 가지치기를 연상한다고 해서 branches라고 합니다. 자세한 설명은 바로 아래 절을 참고하시면 됩니다.
  • tag : trunk와 branches의 특정 시간을 가리키는 포인터입니다. 주로 프로젝트의 릴리즈 주기에 맞추어서, 각 주기의 코드를 보존하기 위해서 사용합니다. 예컨데 beta, alpha, RC, RTM 등이 주요 릴리즈 주기가 될 겁니다. tag는 보통 해당 릴리즈 주기에서 가장 안정화 됐다고 판단하는 시점에 적용을 합니다.

branch

프로젝트를 진행하다 보면, 원본 프로젝트를 기반으로 새로운 프로젝트를 만들거나 혹은 원본 프로젝트를 테스트하기 위해서 따로 분리해야할 경우가 생깁니다.

이럴때 branches를 사용합니다.

calc라는 프로젝트를 진행하고 있다고 가정해 보죠. 이 calc 프로그램은 사칙연산만을 수행하고 있습니다. 그런데 실험적으로 루트연산 기능을 넣었습니다. 루트연산 코드는 아직은 실험적인 코드라서 trunk에 아직은 넣을 수 없습니다. 기능을 추가해야 할지도 확실하지 않고요. 추가한다고 해도 충분히 안정화된 다음에 원본 프로젝트에 포함해야 되겠죠.

이 경우 calc 프로젝트를 복사해서 my-calc이라는 branch를 만들어서 프로젝트를 진행합니다.

branch 프로젝트의 완성도가 충분히 높아졌다고 생각되면, 그때 trunk와 merge하면 됩니다.

혹은 새로운 버전의 프로젝트가 개발됐을 때, 이전 버전의 프로젝트를 그대로 진행하기 위해서 사용합니다. Apache 2.2.2 버전을 개발하다가 새로운 기능을 추가해서 2.3으로 버전을 올렸다고 가정해 보겠습니다. 새로운 Apache 버전이 나왔지만, 여전히 많은 사람들이 Apahce 2.2.2 버전을 사용하고 있을 겁니다. 그러니 비록 예전 버전이긴 2.2.2 버전에 대한 버그 패치, 수정등의 작업은 계속 진행이 되야 합니다. 이 때도 branches를 사용할 수 있습니다. 구버전의 Apache를 위한 branches를 구성해서 프로젝트를 진행하는 거죠.

branch는 간단하게 만들 수 있습니다. branch라는 다소 추상적인 용어를 사용해서 어려워하는 것 같은데, 그냥 지금 진행중인 프로젝트를 복사하는 겁니다. 실제 copy 옵션을 이용해서 branch를 만듭니다. copy 옵션으로 my-calc라는 branch 프로젝트를 만드는 방법입니다.
$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/branches/my-calc-branch \
      -m "Creating a private branch of /calc/trunk."

이제 my-calc-branch라는 이름의 새로운 프로젝트를 만들었습니다. 물론 calc 프로젝트의 디렉토리와 파일들이 그대로 복사됩니다.

<img src="https://docs.google.com/drawings/pub?id=1Ot8hW9DYzU9fYiQr3qPYNSTHYIWvVxmlNbO_H5jIe_8&amp;w=273&amp;h=367">

branch는 독립적인 프로젝트로 checout 해서 사용하면 됩니다. 기타 사용방법도 전혀다를 바가 없습니다.
# svn checkout http://svn.example.com/repos/calc/branches/my-calc-branch
A  my-calc-branch/Makefile
A  my-calc-branch/integer.c
A  my-calc-branch/button.c
Checked out revision 341.

프로젝트 로그 확인

충돌 해결

소스코드 복구

merge

저장소 정책

myTeam에서 소스코드와 문서를 관리하기 위해서 svn 저장소를 운영하기로 했습니다. 진행하는 프로젝트는 Proj01, Proj02, Proj03 이고 문서는 Documents라는 디렉토리 밑에 프로젝트 이름별로 하위 디렉토리를 만들어서 관리하기로 했습니다.

어떤식으로 구성하는게 좋을까요.아래와 같이 trunk, branch, tag를 최상위 디렉토리로 하고 그 아래에 프로젝트를 두는 방법이 있을 겁니다.

반대로 프로젝트를 최상단에 두고, 프로젝트 별로 trunk, branch, tag를 둘 수도 있습니다.

성향에 따라서 호불호가 갈릴 것 같긴합니다만, 프로젝트 이름을 제일 위로 빼는게 눈에 좀 잘들어올 것 같습니다.