SVN이란?
SVN은 중앙 집중식 버전 관리 시스템으로, 하나의 중앙 서버에 모든 변경 내역이 저장된다는 특징이 있다. 그렇기 때문에 SVN 서버에 매우 의존적일 수밖에 없다. 만약 SVN이 문제가 발생할 경우 최신 버전을 가져올 수 없어 작업이 전체 중단될 수 있고, 실수로 파일을 삭제하거나 커밋을 잘못하기라도 한다면 모든 개발자들에게 영향을 미칠 수 있어 큰 장애로 이어질 수 있다. 또한, 브랜치를 생성하고 이를 병합하는 과정이 복잡하여 유연한 브랜치 전략을 사용하기 어렵다.
서버가 중앙에서 전부 관리된다는 것에 대한 장점도 분명히 존재하지만, 앞서 언급한 치명적인 단점들 때문에 많은 개발 팀은 Git으로 전환하는 추세이다.
Git이란?
Git은 분산 버전 관리 시스템으로, 각 개발자가 전체 저장소를 로컬 환경에 복제하여 작업하기 때문에 중앙 서버가 다운되더라도 작업을 계속할 수 있어 개발 속도가 SVN에 비해 빨라진다. 또한, Git은 지원하는 기능이 다양하기 때문에 브랜치를 생성하고 병합하는 과정에서 다양한 브랜치 전략을 적용할 수 있어 개발 프로세스를 체계적으로 관리할 수 있다.
SVN과 Git 저장 방식 차이
- SVN은 델타 방식을 사용하는데, 이 방식은 이전 버전과의 변경된 사항만 기록하여 저장하는 방식이다.
- Git은 스냅샷 방식을 사용하는데, 이 방식은 파일의 모든 상태를 저장하는 방식이다.
두 개의 방식 중 Git의 스냅샷 방식이 속도가 빠르다. 이유는 다음 그림을 통해 설명한다.
델타 방식
각 버전별로 변경사항에 대한 내용만 저장한다.
스냅샷 방식
각 버전별로 전체 파일에 대한 상태를 저장한다. 이때 이전 버전의 파일 내용이 달라지지 않으면 이전 상태 파일에 대한 링크만 저장하여 효율적으로 저장한다.
위 그림만 봤을 때는 델타 방식이 더 효율적이고 속도가 빠를 것 같다 느낄 수 있다. 하지만 델타 방식은 10번 파일이 수정되어 10번째의 상태를 불러와야 한다면 1번 상태부터 10번 상태까지 변화를 순차적으로 전부 연산하여 불러와야 한다. 하지만 스냅샷 방식은 전체 상태를 저장하고 있기 때문에 10번째 상태만 불러오면 되기 때문에 속도가 더 빠른 것이다.
이번 글에서는 SVN환경을 구성하고 Git으로 마이그레이션 하는 실습을 다룬다.
구성 환경
- Amazon EC2 Instance
구성 버전
- EC2 Instance : Amazon2023-6.1.59-84.139.amzn2023.x86_64
- git : 2.40.1
- svn : 1.14.2
전제 조건
- Github 계정 & 레포지토리 생성
1. SVN 환경 구성
svn 환경 구성을 위해 패키지를 다운로드한다.
$ yum install subversion -y
svn에서 사용할 디렉터리를 생성한다.
$ mkdir /home/svn
$ cd /home/svn
snv에서 사용할 저장소를 생성한다.
$ svnadmin create --fs-type fsfs /home/svn/repos
$ ls
repos
저장소 디렉터리에 대한 적절한 권한을 부여 후 svn을 실행한다.
$ chmod –R 760 /home/svn/*
$ svnserve -d -r /home/svn/
# 정상 실행 확인.
$ ps -ef | grep svn | grep -v grep
root 1000399 1 0 09:27 ? 00:00:00 svnserve -d -r /home/svn/
svn이 정상 실행되었는지 확인한다.
$ svn checkout svn://{svn_서버_ip}/repos
Checked out revision 0.
'svnadmin create' 명령으로 생성한 디렉터리로 이동하여 svn 설정파일을 수정해야 한다.
먼저 사용자가 특정 경로에 대해 권한을 가질 수 있게 설정한다.
- test1, test2, test3 유저는 usergroup에 속하고 usergroup에는 경로(/)에 대해 rw(read, write) 권한을 부여한다.
$ cd repos/
$ vi conf/authz
[groups]
usergroup = test1, test2, test3
[/]
@usergroup = rw
사용자 계정과 비밀번호를 정의하여 사용자를 생성한다. 여기서는 간단하게 계정, 비밀번호를 설정한다.
$ vi conf/passwd
[users]
test1 = 1111
test2 = 2222
test3 = 3333
사용자 인증, 접근 제어 등을 설정한다.
- 'anon-access = none' : 익명 사용자 접근을 제한한다.
- 'auth-access = write' : 인증된 사용자 접근에 대해 rw 권한을 허용한다.
- 'password-db = passwd' : 사용자 계정, 비밀번호가 저장된 파일 경로를 지정한다. (conf/passwd)
$ vi conf/svnserve.conf
[general]
anon-access = none
auth-access = write
password-db = passwd
svn에서 편집이 필요한 작업을 할 때 사용할 편집기를 지정한다.
$ vi ~/.bash_profile
...
SVN_EDITOR=/usr/bin/vim
export SVN_EDITOR
$ source ~/.bash_profile
svn 서버 저장소 내에 표준 구조의 디렉터리를 생성한다. (생략해도 됨)
- 표준 구조 디렉터리 : branches, trunk, tags
- branches : 새로운 기능, 버그 수정을 위한 독립적인 작업 공간
- tags : 특정 시점의 스냅샷과 버전 관리를 위한 공간
- trunk : 주요 개발을 위한 공간
$ svn mkdir svn://{svn_서버_ip}/repos/tags
아래와 같이 나오면 ':wq'로 편집기를 저장한다.
'c'를 입력하여 계속 진행한다.
그럼 아래와 같이 Username/Password 입력 후 committed revision이 생성된다.
Authentication realm: <svn://{svn_서버_ip}:3690> f75c2af8-2f67-4d73-8c20-680dd61c376b
Username: test1
Password for 'test1': ****
Committing transaction...
Committed revision 1.
나머지 branches 디렉터리와 trunk 디렉터리도 위와 같이 진행하여 생성한다.
$ svn mkdir svn://{svn_서버_ip}/repos/branches
$ svn mkdir svn://{svn_서버_ip}/repos/trunk
svn list로 잘 생성되었는지 확인한다.
$ svn list svn://{svn_서버_ip}/repos/
Authentication realm: <svn://{svn_서버_ip}:3690> f75c2af8-2f67-4d73-8c20-680dd61c376b
Password for 'test1': ****
branches/
tags/
trunk/
실제 로컬 환경에서 디렉터리 생성을 위해 checkout 한다.
로그인은 위에서 생성한 test1 유저로 로그인한다.
$ svn checkout svn://{svn_서버_ip}/repos
Authentication realm: <svn://{svn_서버_ip}:3690> f75c2af8-2f67-4d73-8c20-680dd61c376b
Password for 'test1': ****
A repos/branches
A repos/tags
A repos/trunk
Checked out revision 3.
실제 디렉터리가 생성되었는지 확인한다.
$ cd /home/svn/repos/repos
$ ls
branches tags trunk
커밋을 생성하여 svn 저장소에 반영을 위해 파일을 생성한다.
$ echo "test1 SVN" > test1.txt
버전 관리 대상으로 추가하기 위해 다음 명령을 실행한다.
$ svn add test1.txt
A test1.txt
svn 저장소에 변경 사항을 반영한다.
$ svn commit -m "Added test1.txt"
Authentication realm: <svn://{svn_서버_ip}:3690> 8b19cc47-0de5-4e6d-b3c1-3a0bca2dc876
Password for 'test': ******
Adding test1.txt
Transmitting file data .done
Committing transaction...
Committed revision 4.
이번에는 test2 유저로 checkout 하여 위 절차와 동일하게 진행한다.
$ svn checkout svn://{svn_서버_ip}/repos --username test2 --password 2222
A repos/branches
A repos/tags
A repos/test1.txt
A repos/trunk
Checked out revision 4.
$ echo " test2 SVN " > test2.txt
$ svn add test2.txt
A test2.txt
$ svn commit -m "Added test2.txt"
Authentication realm: <svn://{svn_서버_ip}:3690> f75c2af8-2f67-4d73-8c20-680dd61c376b
Password for 'test2': ****
Adding test2.txt
Transmitting file data .done
Committing transaction...
Committed revision 5.
이번에는 test3 유저로 checkout 하여 위 절차와 동일하게 진행한다.
$ svn checkout svn://{svn_서버_ip}/repos --username test3 --password 3333
A repos/test2.txt
Checked out revision 5.
$ echo " test3 SVN " > test3.txt
$ svn add test3.txt
A test3.txt
$ svn commit -m "Added test3.txt"
Authentication realm: <svn://{svn_서버_ip}:3690> f75c2af8-2f67-4d73-8c20-680dd61c376b
Password for 'test3': ****
Adding test3.txt
Transmitting file data .done
Committing transaction...
Committed revision 6.
전체 로그를 확인하여 유저별 커밋 메세지를 확인한다.
$ svn log -r HEAD:1
# 또는
$ svn log svn://{svn_서버_ip}/repos --username test1 --password 1111
------------------------------------------------------------------------
r6 | test3 | 2024-06-24 11:54:47 +0000 (Mon, 24 Jun 2024) | 1 line
Added test3.txt
------------------------------------------------------------------------
r5 | test2 | 2024-06-24 11:51:17 +0000 (Mon, 24 Jun 2024) | 1 line
Added test2.txt
------------------------------------------------------------------------
r4 | test1 | 2024-06-24 11:48:22 +0000 (Mon, 24 Jun 2024) | 1 line
Added test1.txt
------------------------------------------------------------------------
r3 | test1 | 2024-06-24 11:26:43 +0000 (Mon, 24 Jun 2024) | 1 line
------------------------------------------------------------------------
r2 | test1 | 2024-06-24 11:24:41 +0000 (Mon, 24 Jun 2024) | 1 line
------------------------------------------------------------------------
r1 | test1 | 2024-06-24 11:23:06 +0000 (Mon, 24 Jun 2024) | 1 line
------------------------------------------------------------------------
유저별 커밋 메세지를 확인할 수 있다.
2. SVN → Git 마이그레이션
git 마이그레이션을 위한 패키지를 다운로드한다.
$ yum install -y git-svn
마이그레이션에 필요한 사용자 정보 파일(authors-file)을 작성한다.
- authors.txt
- 왼쪽부터 : svn 유저/ git 유저 / email 주소
test1 = test1 <test1@example.com>
test2 = test2 <test2@example.com>
test3 = test3 <test3@example.com>
git svn 명령어로 사용자 정보 파일을 지정하여 마이그레이션을 진행한다.
- SVN을 표준 구조를 따라 구성 및 사용하고 있는 경우 '--stdlayout' 옵션을 추가하고, 그렇지 않은 경우 생략한다.
$ git svn clone --authors-file=authors.txt svn://{svn_서버_ip}/repos --username test1 ./git_migration
W: +empty_dir: branches
r1 = ce0a582501f5bb032d0bea27f2eab7dc8a0de556 (refs/remotes/git-svn)
W: +empty_dir: trunk
r2 = e838b401e605106bb4fd3869a0ffb5f80266f4e4 (refs/remotes/git-svn)
W: +empty_dir: tags
r3 = a96c6c11f713a5476dd7785e21beeea8f0f3dba6 (refs/remotes/git-svn)
A test1.txt
r4 = f5e7ca793871db840eb3e727a6f11234f0369f60 (refs/remotes/git-svn)
A test2.txt
r5 = 153b65439204642c145c5a8823796f2ccd82523d (refs/remotes/git-svn)
A test3.txt
r6 = 8a55fc98bfeefab79235a7e33814c4f60395ba5f (refs/remotes/git-svn)
Checked out HEAD:
svn://{svn_서버_ip}/repos r6
creating empty directory: branches
creating empty directory: tags
creating empty directory: trunk
git_migration 디렉터리에 정상적으로 파일들이 생성되었는지 확인한다.
$ cd ./git_migration
$ ls
branches tags test1.txt test2.txt test3.txt trunk
실제 git의 commit이 정상 생성 되었는지 git 명령어로 확인한다.
$ git log
commit 8a55fc98bfeefab79235a7e33814c4f60395ba5f (HEAD -> master, git-svn)
Author: test3 <test3@example.com>
Date: Mon Jun 24 11:54:47 2024 +0000
Added test3.txt
git-svn-id: svn://{svn_서버_ip}/repos@6 f75c2af8-2f67-4d73-8c20-680dd61c376b
commit 153b65439204642c145c5a8823796f2ccd82523d
Author: test2 <test2@example.com>
Date: Mon Jun 24 11:51:17 2024 +0000
Added test2.txt
git-svn-id: svn://{svn_서버_ip}/repos@5 f75c2af8-2f67-4d73-8c20-680dd61c376b
commit f5e7ca793871db840eb3e727a6f11234f0369f60
Author: test1 <test1@example.com>
Date: Mon Jun 24 11:48:22 2024 +0000
Added test1.txt
git-svn-id: svn://{svn_서버_ip}/repos@4 f75c2af8-2f67-4d73-8c20-680dd61c376b
commit a96c6c11f713a5476dd7785e21beeea8f0f3dba6
Author: test1 <test1@example.com>
Date: Mon Jun 24 11:26:43 2024 +0000
git-svn-id: svn://{svn_서버_ip}/repos@3 f75c2af8-2f67-4d73-8c20-680dd61c376b
commit e838b401e605106bb4fd3869a0ffb5f80266f4e4
Author: test1 <test1@example.com>
Date: Mon Jun 24 11:24:41 2024 +0000
git-svn-id: svn://{svn_서버_ip}/repos@2 f75c2af8-2f67-4d73-8c20-680dd61c376b
commit ce0a582501f5bb032d0bea27f2eab7dc8a0de556
Author: test1 <test1@example.com>
Date: Mon Jun 24 11:23:06 2024 +0000
git-svn-id: svn://{svn_서버_ip}/repos@1 f75c2af8-2f67-4d73-8c20-680dd61c376b
git 원격 저장소 등록 후 push 한다.
$ git remote add origin https://github.com/jwhong-3004/svn-to-git.git
$ git push origin master
Username for 'https://github.com': jwhong-3004
Password for 'https://jwhong-3004@github.com':
Enumerating objects: 13, done.
Counting objects: 100% (13/13), done.
Delta compression using up to 4 threads
Compressing objects: 100% (8/8), done.
Writing objects: 100% (13/13), 1.15 KiB | 1.15 MiB/s, done.
Total 13 (delta 3), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (3/3), done.
remote:
remote: Create a pull request for 'master' on GitHub by visiting:
remote: https://github.com/jwhong-3004/svn-to-git/pull/new/master
remote:
To https://github.com/jwhong-3004/svn-to-git.git
* [new branch] master -> master
git 저장소에 접속하여 파일들이 push 되었는지 확인한다.
commit 메세지도 마이그레이션 되었는지 확인한다.
마무리
SVN은 오랜 기간 동안 많은 개발 팀에서 사용하는 버전 관리 시스템으로 자리 잡았지만, 개발 환경이 점점 더 복잡해지고 협업이 중요해지면서 Git이 제공하는 분산형 버전 관리의 장점은 무시할 수 없게 되었다. Git의 도입은 단순히 버전 관리 도구를 바꾸는 것이 아닌 협업을 강화하여 코드 품질을 높이고, 더 빠르고 효율적인 개발 프로세스를 구현하여 보다 나은 개발 문화를 만들기 위한 중요한 과정이라고 생각한다.
댓글