2017-01-31
해당 글은 필자가 SVN을 리눅스에 설치하고 간단한 사용법을 사용하면서 겪었던 일들을 두서 없이 나열한 것이다.
SVN의 명령어나 사용법에 대해 어느정도 배경 지식이 있는채로 쓴 글이라는 점을 유념 해두고, 명령어와 SVN에 대한 상세한 설명은 아래 링크들을 참조 하길 바란다.

참조1 : https://wiki.kldp.org/wiki.php/SubversionBook
참조2 : http://svnbook.red-bean.com/en/1.6/


아래 내용은 CentOS 7을 기준으로 쓰여진 글이며, 관련 메세지들은 필자가 테스트 도중 리눅스를 5번정도 깔았다 지우는 사이, 한글 옵션을 집어 넣어서... 첨에는 영어로 메세지가 나오다가 한글로 바뀌었으니, 너무 신경쓰지 말길 바란다.


우선 리스트로 확인하고 설치를 했다.
[root@localhost b1ix]# yum list subversion ...... [root@localhost b1ix]# yum install subversion

일단, 아래 파일 없으면 생성해야 하고, 해당 파일을 있어야 service로 실행 가능하다.(필자는 이미 있었다)
/etc/sysconfig/svnserve
그리고 서비스 시작을 해보니 오류가 뜬다..
[root@localhost b1ix]# service svnserve status Redirecting to /bin/systemctl status svnserve.service svnserve.service - Subversion protocol daemon Loaded: loaded (/usr/lib/systemd/system/svnserve.service; disabled) Active: failed (Result: exit-code) since Tue 2017-01-10 08:31:25 PST; 11s ago Process: 43507 ExecStart=/usr/bin/svnserve --daemon --pid-file=/run/svnserve/svnserve.pid $OPTIONS (code=exited, status=1/FAILURE) Jan 10 08:31:25 localhost.localdomain svnserve[43507]: svnserve: Root path '/var/svn' does not exist or is not a directory. Jan 10 08:31:25 localhost.localdomain systemd[1]: svnserve.service: control process exited, code=exited status=1 Jan 10 08:31:25 localhost.localdomain systemd[1]: Failed to start Subversion protocol daemon. Jan 10 08:31:25 localhost.localdomain systemd[1]: Unit svnserve.service entered failed state.
대충 읽어보니, /var/svn 이라는 폴더가 없다고 한다.

그래서 일단 /home 및에 svn 디렉토리 생성하고,
[root@localhost home]# mkdir svn
위에서 언급된 /etc/sysconfig/svnserve파일에 들어간다.
#vim svnserve
해당 파일에서 '/var/svn'라고 되어 있던 부분을 '/home/svn'로 변경하고 저장한다.
# OPTIONS is used to pass command-line arguments to svnserve. # # Specify the repository location in -r parameter: OPTIONS="-r /home/svn"
다시 서비스를 시작해보니 이번엔 정상적으로 실행된다.
root@localhost home]# service svnserve start Redirecting to /bin/systemctl start svnserve.service

자 이제 본격적으로 저장소를 만들고 svn을 설정해 보자.

우선 저장소 store1을 만들었다.
[root@localhost svn]# svnadmin create --fs-type fsfs store1 [root@localhost svn]# ll total 0 drwxr-xr-x. 6 root root 80 Jan 10 17:49 store1 [root@localhost svn]# cd store1 [root@localhost store1]# ll total 16 drwxr-xr-x. 2 root root 51 Jan 10 17:49 conf drwxr-sr-x. 6 root root 4096 Jan 10 17:49 db -r--r--r--. 1 root root 2 Jan 10 17:49 format drwxr-xr-x. 2 root root 4096 Jan 10 17:49 hooks drwxr-xr-x. 2 root root 39 Jan 10 17:49 locks -rw-r--r--. 1 root root 229 Jan 10 17:49 README.txt
저장소를 만들었으면, svnserve.conf파일로 들어가서, 저장소 설정을 해준다.
[root@localhost store1]# cd conf/ [root@localhost conf]# ll total 12 -rw-r--r--. 1 root root 1080 Jan 10 17:49 authz -rw-r--r--. 1 root root 309 Jan 10 17:49 passwd -rw-r--r--. 1 root root 3090 Jan 10 17:49 svnserve.conf [root@localhost conf]# cp svnserve.conf svnserve.conf.back [root@localhost conf]# ll total 16 -rw-r--r--. 1 root root 1080 Jan 10 17:49 authz -rw-r--r--. 1 root root 309 Jan 10 17:49 passwd -rw-r--r--. 1 root root 3090 Jan 10 17:49 svnserve.conf -rw-r--r--. 1 root root 3090 Jan 10 18:06 svnserve.conf.back [root@localhost conf]# echo '[general]' > svnserve.conf [root@localhost conf]# 'anon-access = none' >> svnserve.conf [root@localhost conf]# vim svnserve.conf auth-access = write password-db = passwd authz-db = authz
svnserve.conf의 기본 형식 파일을 하나 복사해서 .back으로 해두고, 설정을 시작했다.
echo로 입력하다가 귀찮아서, 그냥 vim으로 들어가서 복붙을 했다.
위에도 말했다 싶이 관련 설정들에 대한 자세한 설명들은, 위의 링크나 타 사이트를 찾아 보길 바란다.

- anon-access : 로그인하지 않은 사용자에 대한 접근권한 (read/write/none 선택)
- auth-access : 로그인한 사용자에 대한 접근 권한 설정 (read/write/none 선택)
- passwd-db : 저장소에 접근할 사용자계정과 비밀번호를 관리할 파일 지정 (기본 passwd)
- authz-db : 파일과 디렉터리에 대한 접근 권한을 관리하는 파일 지정 (기본 authz)


자 다음은 passwd에 들어가서 사용자 생성해보자.
[root@localhost conf]# vim passwd [users] # harry = harryssecret # sally = sallyssecret test1 = test1
왜 svn문서에서 해리랑 샐리를 언급하는지 여기서 알 수 있다.
필자는 test1이라는 계정을 하나 만들어 두었다.
자 이제 authz파일을 설정할 차례 이다.
[root@localhost conf]# vim authz [store1:/] * = rw
store1에 접근하는 모두에게 rw권한을 준다. 그런 후에 svn을 다시 시작해 보자..
여기서 알아둘 것이 있는데, 위에서 언급한거처럼 필자는 리눅스를 여러번 다시 깔면서 실험을 했다.
그랬더니, 전에는 발생하지 않았던 아래와 같은 오류가 떴다.
[root@localhost ~]# systemctl start svnserve Job for svnserve.service failed because the control process exited with error code. See "systemctl status svnserve.service" and "journalctl -xe" for details.
해당 오류 발생시,
[root@localhost conf]# systemctl list-unit-files | grep svnserve svnserve.service disabled
위와 같은 명령어를 치면, 해당 서비스가 disabled 되어 있는걸 볼 수 있다.
[root@localhost conf]# systemctl enable svnserve.service Created symlink from /etc/systemd/system/multi-user.target.wants/svnserve.service to /usr/lib/systemd/system/svnserve.service. [root@localhost conf]# systemctl list-unit-files | grep svnserve svnserve.service enabled
위와 같이 enabled 시키면 잘 시작 된다.
그 외에 아래와 같은 오류가 뜰 수 도 있다.
[root@localhost conf]# systemctl status svnserve ● svnserve.service - Subversion protocol daemon Loaded: loaded (/usr/lib/systemd/system/svnserve.service; enabled; vendor preset: disabled) Active: failed (Result: exit-code) since 목 2017-01-12 12:24:03 KST; 1min 18s ago Process: 54450 ExecStart=/usr/bin/svnserve --daemon --pid-file=/run/svnserve/svnserve.pid $OPTIONS (code=exited, status=1/FAILURE) 1월 12 12:24:03 localhost.localdomain systemd[1]: Starting Subversion protocol daemon... 1월 12 12:24:03 localhost.localdomain svnserve[54450]: svnserve: E000098: 서버소켓을 바인드할 수 없습니다: 주소가 이미 사용 중입니다 1월 12 12:24:03 localhost.localdomain systemd[1]: svnserve.service: control process exited, code=exited status=1 1월 12 12:24:03 localhost.localdomain systemd[1]: Failed to start Subversion protocol daemon. 1월 12 12:24:03 localhost.localdomain systemd[1]: Unit svnserve.service entered failed state. 1월 12 12:24:03 localhost.localdomain systemd[1]: svnserve.service failed.
/etc/sysconfig/svnserve 파일에서 설정한 값과 별개로 svnserve -d -r /home/svn 로 해당 폴더를 설정 할 수 있는데, 이렇게 두곳에서 설정을 해버리면, 이미 사용중이라는 식으로 에러가 뜰 수 있다. 보통 재부팅 하면 해결 된다.
[root@localhost ~]# systemctl start svnserve [root@localhost ~]# ps -aux | grep svnserve root 1374 0.0 0.0 166324 924 ? Ss 12:26 0:00 /usr/bin/svnserve --daemon --pid-file=/run/svnserve/svnserve.pid -r /home/svn root 2327 0.0 0.1 116812 1012 pts/1 R+ 12:30 0:00 grep --color=auto svnserve [root@localhost ~]#
자 이제, 저장소 설정이 다 끝났으니, 실제로 해당 저장소를 checkout 해보자.
참고로 필자가 가장 삽질을 많이 한 부분이 이곳인데, 이제부터 필자의 삽질을 공개하겠다....

[root@localhost checkout1]# svn checkout svn://localhost/svn_repos
checkout을 하려고 보니, svn에 접속 권한이 없다고 뜨길래.. 일단 svnserv데몬부터 살펴 보았다.
[root@localhost svn]# ps -aux | grep svnserv root 62709 0.0 0.1 199280 1400 ? Ss 22:29 0:00 /usr/bin/svnserve --daemon --pid-file=/run/svnserve/svnserve.pid -r /home/svn/store1 root 63212 0.0 0.0 112640 956 pts/0 R+ 22:31 0:00 grep --color=auto svnserv
데몬은 잘 돌아가고 있었다.
그래서 다음으로 생각한게 방화벽!! 왠지 뭔가 안되면 방화벽부터 살펴보는 습관이 들어서....
$ service iptables status Redirecting to /bin/systemctl status iptables.service iptables.service Loaded: not-found (Reason: No such file or directory) Active: inactive (dead)
방화벽 상태를 보니, 방화벽은 안켜져 있는데, 생각해보니 이건 CentOS 7이었고, CentOS 7은 firewalld라는 개별 방화벽이 따로 있어서 아래와 같이 멈춰 놓았다.
[root@localhost b1ix]# systemctl stop firewalld
눈치 채신분도 있겠지만, localhost로 접속하는데 방화벽이 무슨 상관이겠는가? 필자처럼 방화벽을 함부로 끄진 말길 바란다..
자 이제 다시 checkout을 해보자
[root@localhost checkout1]# svn checkout svn://localhost/svn_repos svn: E000013: Unable to connect to a repository at URL 'svn://localhost/svn_repos' svn: E000013: file '/home/svn/svn_repos/format' 를 열 수 없습니다: 허가 거부
format 파일에 접근이 안된단다... 그렇다면, 일단 권한을 다 줘보자..
[root@localhost svn_repos]# chmod 777 format [root@localhost svn_repos]# ll 합계 8 -rw-r--r--. 1 root root 229 1월 12 11:58 README.txt drwxr-xr-x. 2 root root 80 1월 12 12:08 conf drwxr-sr-x. 6 root root 233 1월 12 11:58 db -rwxrwxrwx. 1 root root 2 1월 12 11:58 format drwxr-xr-x. 2 root root 231 1월 12 11:58 hooks drwxr-xr-x. 2 root root 41 1월 12 11:58 locks [root@localhost checkout1]# svn checkout svn://localhost/svn_repos svn: E000013: Unable to connect to a repository at URL 'svn://localhost/svn_repos' svn: E000013: file '/home/svn/svn_repos/format' 를 열 수 없습니다: 허가 거부
그래도 안된다, selinux 설정을 보니 활성화가 되어 있다.
[root@localhost /]# sestatus SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: enforcing Mode from config file: disabled Policy MLS status: enabled Policy deny_unknown status: allowed Max kernel policy version: 28 [root@localhost /]# vim /etc/sysconfig/selinux SELINUX=disabled
위처럼 /etc/sysconfig/selinux파일에 들어가서, 해당 설정을 disabled한다.
selinux는 원래 보안상 중요해서 함부로 끄면 안되고, selinux를 잘 설정해 놓아야 하지만, 필자는 localhost에서만 돌리고 있기 때문에 그냥 꺼버렸다.
이 글을 보고 계신 분은 selinux를 끄지 않고, 해당 format파일에 권한을 설정해 주어야 할 것이다.
그리고 reboot 한다.
[root@localhost checkout1]# sestatus SELinux status: disabled
위처럼 selinux가 꺼진게 보인다. 이제 checkout을 다시 해보면.. 잘 된다.
[root@localhost /]# cd /home/svn_checkout/checkout1/ [root@localhost checkout1]# svn checkout svn://localhost/svn_repos 인증 영역(realm): 2ca9c2f6-5e56-4daf-8f4a-4496e6cefbd2 'root'의 암호: 인증 영역(realm): 2ca9c2f6-5e56-4daf-8f4a-4496e6cefbd2 사용자명:test1 'test1'의 암호: ----------------------------------------------------------------------- 주의! 인증정보 영역: 2ca9c2f6-5e56-4daf-8f4a-4496e6cefbd2 에 대한 당신의 비밀번호는 디스크에 암호화되어 저장되지 않습니다. 가능하면, 비밀번호를 암호화하여 저장하도록 설정을 바꾸십시오. 자세한 것은 문서를 참조하세요. 이 주의 문구를 다음에 보이지 않게 하려면, 'store-plaintext-passwords'의 설정을 'yes' 혹은 'no'로 지정하면 됩니다. 설정 파일은 다음과 같습니다. '/root/.subversion/servers' ----------------------------------------------------------------------- 비밀번호를 평문으로 저장하겠습니까 (yes/no)?yes 체크아웃된 리비전 0.
자 이제 기본 폴더들을 만들어 보자
[root@localhost home]# svn mkdir svn://localhost/svn_repos/trunk svn: E205007: 로그 메시지를 구하기 위해 외부 프로그램을 사용할 수 없습니다. SVN_EDITOR 환경변수를 설정하시거나 --message (-m) 또는 --file (-F) 옵션을 사용하세요 svn: E205007: 환경변수 SVN_EDITOR, VISUAL, EDITOR 중 하나는 설정하거나, 'editor-cmd' 를 구성화일에 명시해야합니다
에러가 뜬다..
환경변수중 SVN_EDITOR가 없다고 한다.
해당 설정은 commit시에 로그를 남기기 위해서 필요하다.
[root@localhost /]# vi ~/.bash_profile # .bash_profile # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi # User specific environment and startup programs PATH=$PATH:$HOME/bin export PATH SVN_EDITOR=/usr/bin/vim export SVN_EDITOR
위와 같이 맨 아랫줄에 SVN_EDITOR 관련 설정 2줄을 넣어 준다.
[root@localhost /]# source ~/.bash_profile
그리고 source 명령어로 재시작 하지 않고도 적용 되도록 한다.
그리고 이제 폴더를 다시 만들어 보자
[root@localhost /]# svn mkdir svn://localhost/svn_repos/trunk 커밋된 리비전 1. [root@localhost /]# svn mkdir svn://localhost/svn_repos/branche1 커밋된 리비전 2. [root@localhost /]# svn mkdir svn://localhost/svn_repos/tag1 커밋된 리비전 3.
mkdir branche1 --이 줄 이하는 자동으로 제거됩니다-- A svn://localhost/svn_repos/branche1
명령어를 친 이후에 위와 같은 화면이 뜰텐데, ㅡm 옵션으로 로그를 적지 않으면 적으라고 뜬다.
"--이 줄 이하는 자동으로 제거됩니다--"라고 되어 있는 곳 위에다가 로그를 적어 주면 된다.
자 이제 잘 만들어 졌나 확인해보면..
[root@localhost /]# svn list svn://localhost/svn_repos branche1/ tag1/ trunk/
잘 만들어 졌다.

자 이제 update를 해보자.
[root@localhost /]# cd /home/svn_checkout/checkout1/svn_repos/ [root@localhost svn_repos]# svn update Updating '.': A tag1 A branche1 A trunk 업데이트 된 리비전 3.
해당 폴더에 svn_repos 디렉터리에서 만들었기 때문에, 해당 디렉터리로 가서 upate를 해준다. 다른 폴더에서 하면, 충돌이 일어난다.
[root@localhost svn_repos]# cd .. [root@localhost checkout1]# svn update 무시함 '.' 충돌 상황 요약: 무시한 경로수: 1
[root@localhost checkout1]# cd svn_repos/ [root@localhost svn_repos]# ll 합계 0 drwxr-xr-x 2 root root 6 1월 13 17:05 branche1 drwxr-xr-x 2 root root 6 1월 13 17:05 tag1 drwxr-xr-x 2 root root 6 1월 13 17:05 trunk
다시 원래의 checkout된 폴더로 가서 확인하면 잘 업데이트 된걸 확인 할 수 있다.

자.. 이제 svn의 유용한 기능들을 체험하기 위해서, branch를 만들고, merge를 해보자
이 부분부터는 위에 필자가 참고하라고 한 링크를 보지 않고는 이해가 안가는 부분이 많을 수 있으니 주의 바란다.
우선 미리 만들어 놓은 file1을 커밋 해보자
[root@localhost trunk]# cd /home/svn_checkout/checkout1/svn_repos/trunk [root@localhost trunk]# svn commit file1 -m 'file1 commit' svn: E200009: 커밋이 실패하였습니다: svn: E200009: '/home/svn_checkout/checkout1/svn_repos/trunk/file1' 은(는) 버전 컨트롤 대상이 아닙니다
위처럼 에러가 뜰것이다.
file1은 임의로 만든 파일이다. 새로 만든 파일이기 때문에 add후에 커밋을 해줘야 한다.
[root@localhost trunk]# svn add file1 A file1 [root@localhost trunk]# svn commit file1 -m 'file1 commit' 추가 file1 파일 데이터 전송중 . 커밋된 리비전 4.
add 한후에 하니 제대로 commit이 되었다.
[root@localhost trunk]# cd /home/svn_checkout/checkout2/svn_repos/trunk [root@localhost trunk]# svn update Updating '.': A file1 업데이트 된 리비전 4. [root@localhost trunk]# ll 합계 4 -rw-r--r-- 1 root root 66 1월 18 15:58 file1
필자가 만들어놓은 다른 위치(checkout2)에서 update 해보니 잘 되는 것을 볼 수 있다.

자 이제 좀더 이것저것 해보기 위해서 파일을 좀더 만들어 놓고 커밋을 해보겠다.
[root@localhost trunk]# cd /home/svn_checkout/checkout1/svn_repos/trunk [root@localhost trunk]# mkdir dir1 [root@localhost trunk]# cd dir1/ [root@localhost dir1]# vim file2 [root@localhost dir1]# cd .. [root@localhost trunk]# svn add dir1 A dir1 A dir1/file2 [root@localhost trunk]# svn commit ./dir1/ 추가 dir1 추가 dir1/file2 파일 데이터 전송중 . 커밋된 리비전 5.
자 이렇게 dir1과 file2를 만들고 add 한뒤에 commit을 했다.
그리고 다른 곳에서 업데이트를 받아보자
[root@localhost trunk]# cd /home/svn_checkout/checkout2/svn_repos/trunk [root@localhost trunk]# svn update Updating '.': A dir1 A dir1/file2 업데이트 된 리비전 5.
자 이제 준비는 끝났으니 branch를 만들어 보자.

branch를 만들려고 보니, 필자가 처음 만들었던 폴더 이름중, 이름의 branche1가 e가 들어가서 오타가 있는걸 발견 했다.
그래서 e가 없는 폴더를 만들고 해당 폴더는 지웠다.
[root@localhost home]# svn list svn://localhost/svn_repos branche1/ tag1/ trunk/ [root@localhost home]# svn mkdir svn://localhost/svn_repos/branch1 커밋된 리비전 6. [root@localhost home]# svn list svn://localhost/svn_repos branch1/ branche1/ tag1/ trunk/ [root@localhost home]# svn delete svn://localhost/svn_repos/branche1 커밋된 리비전 7. [root@localhost home]# svn list svn://localhost/svn_repos branch1/ tag1/ trunk/
필자가 svn mkdir을 사용하면 자동으로 커밋이 이루어 지는걸 알 수 있는데, 커밋시에는 반드시 -m 으로 메세지를 적게 되어 있다.
하지만 필자는 위에서 vim과 연동시켜 놓았기에, -m을 적지 않으면 자동으로 vim에서 커밋 메세지를 적도록 화면이 뜬다는걸 알아두길 바란다.

자 그러면 branch를 하나 만들어 보자.
[root@localhost home]# svn copy svn://localhost/svn_repos/trunk svn://localhost/svn_repos/branch1/test_branch1 -m 'create test_branch1' 커밋된 리비전 10. [root@localhost home]# svn list svn://localhost/svn_repos/branch1/test_branch1 dir1/ file1
svn copy 명령어를 이용하여, svn_repos/branch1 폴더 아래에 trunk의 내용을 그대로 가지는 test_branch1을 만든다.
svn list 명령어로 확인결과 trunk의 내용이 잘 copy된 걸 알 수 있다.

이번엔 svn copy시에 이전 리비전을 copy하는걸 해보자.
이쯤 되면 알겠지만, svn에서 branch나 tag는 다 같은 copy일 뿐이다, 다만 사용하는 방식에 따라서 구분을 해놓고 있을 뿐이다.
이전 리비전중 원하는 리비전이 어느부분인지 알기 위해서 우선 log를 확인해 보자.
[root@localhost trunk]# svn log svn://localhost/svn_repos ------------------------------------------------------------------------ r10 | test1 | 2017-01-19 14:33:21 +0900 (2017-01-19, 목) | 1 개의 행 create test_branch1 ------------------------------------------------------------------------ r9 | test1 | 2017-01-19 14:25:19 +0900 (2017-01-19, 목) | 3 개의 행 delete test_dir1 ------------------------------------------------------------------------ r8 | test1 | 2017-01-19 14:24:08 +0900 (2017-01-19, 목) | 1 개의 행 mkdir test_dir1 ------------------------------------------------------------------------ r7 | test1 | 2017-01-19 14:20:02 +0900 (2017-01-19, 목) | 2 개의 행 delete branche1 ------------------------------------------------------------------------ r6 | test1 | 2017-01-19 14:17:15 +0900 (2017-01-19, 목) | 2 개의 행 mkdir branch1 ------------------------------------------------------------------------ r5 | test1 | 2017-01-19 13:54:26 +0900 (2017-01-19, 목) | 2 개의 행 commit dir1, dir1/file2 ------------------------------------------------------------------------ r4 | test1 | 2017-01-18 15:57:27 +0900 (2017-01-18, 수) | 1 개의 행 file1 commit ------------------------------------------------------------------------ r3 | test1 | 2017-01-13 15:18:20 +0900 (2017-01-13, 금) | 2 개의 행 mkdir tag1 ------------------------------------------------------------------------ r2 | test1 | 2017-01-13 15:17:40 +0900 (2017-01-13, 금) | 2 개의 행 mkdir branche1 ------------------------------------------------------------------------ r1 | test1 | 2017-01-13 15:15:10 +0900 (2017-01-13, 금) | 1 개의 행 ------------------------------------------------------------------------
로그를 확인 했으면 필자가 원하는 리비전 4로 branch를 하나 만들어 보겠다.
[root@localhost trunk]# svn copy -r 4 svn://localhost/svn_repos/trunk svn://localhost/svn_repos/branch1/test_branch_r4 -m 'create revision 4 trunk branch' 커밋된 리비전 11.
이번에는 revision 4로 만들어진 브랜치를 체크아웃 하겠다.
[root@localhost svn_checkout]# cd /home/svn_checkout/ [root@localhost svn_checkout]# mkdir branch_checkout_r_4 [root@localhost svn_checkout]# cd branch_checkout_r_4/ [root@localhost branch_checkout_r_4]# svn checkout svn://localhost/svn_repos/branch1/test_branch_r4 A test_branch_r4/file1 체크아웃된 리비전 11. [root@localhost branch_checkout_r_4]# cd test_branch_r4/ [root@localhost test_branch_r4]# ll 합계 4 -rw-r--r-- 1 root root 66 1월 20 17:26 file1
자 이제 revision4로 만들어진 branch를 checkout까지 끝냈으니 merge를 위한 조작을 시작해보자.
조작을 위해 file1과 file2를 수정한뒤 커밋을 해보겠다.
[root@localhost trunk]# svn log -v -r 15:14 svn://localhost/svn_repos ------------------------------------------------------------------------ r15 | test1 | 2017-01-23 17:57:20 +0900 (2017-01-23, 월) | 1 개의 행 변경된 경로: M /trunk/dir1/file2 M /trunk/file1 file1, file2 modify ------------------------------------------------------------------------ r14 | test1 | 2017-01-23 17:56:33 +0900 (2017-01-23, 월) | 1 개의 행 변경된 경로: M /trunk/dir1/file2 M /trunk/file1 file1, file2 commit ------------------------------------------------------------------------
위와 같은 옵션을 사용할 시에는, 특정 로그만 볼 수 있다.
file1과 file2가 수정되어 기록에 남아이 ㅆ는것을 볼 수 있을 것이다.
여기서 diff를 사용해보면 위 로그를 바탕으로 바뀐점들을 상세히 볼 수도 있다.
[root@localhost trunk]# svn diff -r 15:14 svn://localhost/svn_repos Index: trunk/dir1/file2 =================================================================== --- trunk/dir1/file2 (리비전 15) +++ trunk/dir1/file2 (리비전 14) @@ -1,4 +1,4 @@ A B C -d +D Index: trunk/file1 =================================================================== --- trunk/file1 (리비전 15) +++ trunk/file1 (리비전 14) @@ -1,4 +1,4 @@ a -B +b c d
리비전 14과 15에서 file1과 file2가 어떤식으로 바뀌었는지 표시해 준다.
필자는 file1에서 B를 b로 바꾸었다.
file2에서는 d를 D로 바꾸었다. diff는 그런 변경 사항을 상세히 보여준다.

자 이제 merge를 해보자
[root@localhost trunk]# svn diff -r 15:16 svn://localhost/svn_repos/trunk/file1 Index: file1 =================================================================== --- file1 (리비전 15) +++ file1 (리비전 16) @@ -1,4 +1,4 @@ a B -c +C d [root@localhost trunk]# svn merge -r 15:16 ./file1 --- 병합중: r16 리비전을 'file1'에 병합: U file1 --- r16의 병합정보를 'file1'에 기록중: U file1 [root@localhost trunk]# vim file1 a B C d
file1을 diff 해보면, c를 C로 변경한 것이 보일 것이다. 해당 file1은 아직 update를 받지 않아서, 파일이 변경되지 않았다.
그후 15와 16 리비전을 merge 하면 위와 같이 리비전16의 내용으로 덮어지는걸 알수 있다.
일단 file1을 revert로 다시 되돌려 보자
[root@localhost trunk]# svn revert ./file1 다시 복원함 'file1' [root@localhost trunk]# vim file1 a B c d
원래의 소문자c로 돌아온 것을 볼 수 있다.
그리고 리비전을 반대로 병합하려고 보니,
[root@localhost trunk]# svn merge -r 16:15 ./file1 svn: E195020: 경로의 미래에 대한 내용으로 구간을 역병합을 할 수 없습니다; 업데이트를 먼저 하세요
위와 같이 업데이트를 하라고 뜬다.
그러면 업데이트를 하고 병합을 해보겠다.
[root@localhost trunk]# svn update Updating '.': U file1 업데이트 된 리비전 16. [root@localhost trunk]# svn merge -r 16:15 ./file1 --- 역병합: r16 리비전을 'file1'에 역병합: U file1 --- r16의 역병합을 위한 병합정보를 'file1'에 기록중: U file1 --- 'file1'의 병합정보를 생략하는 중: U file1 [root@localhost trunk]# vim file1 a B c d
파일이 전 파일로 병합되여 c가 다시 소문자 c가 된것을 볼 수 있다.

여기까지는 단순히 이전 리비전으로 되돌리는 작업과 같다.
그럼 서로 다른 branch끼리 충돌이 일어나게 해보겠다.
[root@localhost trunk]# svn copy -r 15 svn://localhost/svn_repos/trunk svn://localhost/svn_repos/branch1/r15_branch -m "r15 branch" 커밋된 리비전 17. [root@localhost trunk]# svn copy -r 16 svn://localhost/svn_repos/trunk svn://localhost/svn_repos/branch1/r16_branch -m "r16 branch" 커밋된 리비전 18. [root@localhost trunk]# svn list svn://localhost/svn_repos/branch1 r15_branch/ r16_branch/ [root@localhost trunk]# svn merge svn://localhost/svn_repos/branch1/r15_branch/file1 svn://localhost/svn_repos/branch1/r16_branch/file1 ./file1 --- 병합중: 두 저장소의 URL의 차이를 'file1'에 병합: G file1 --- 저장소 URL들의 병합정보를 'file1'에 기록중: G file1 [root@localhost trunk]# vim file1 a B C d [root@localhost trunk]# svn merge svn://localhost/svn_repos/branch1/r16_branch/file1 svn://localhost/svn_repos/branch1/r15_branch/file1 ./file1 --- 병합중: 두 저장소의 URL의 차이를 'file1'에 병합: U file1 --- 저장소 URL들의 병합정보를 'file1'에 기록중: G file1 [root@localhost trunk]# vim file1 a B c d
병합되는 순서에 따라, 앞에서 뒤에 나오는 파일이 앞에 나오는 파일로 덮어 씌워 지는걸 볼 수 있다.(필자가 하필 대문자C와 소문자c를 바꾸어 놓아서, 잘 보면 c가 작아지는걸 볼 수 있다)

자 그럼 마지막으로 merge시에 충돌이 일어나는 과정이다.
[root@localhost svn_checkout]# mkdir branch_check_r15 [root@localhost svn_checkout]# mkdir branch_check_r16 [root@localhost svn_checkout]# svn checkout svn://localhost/svn_repos/branch1/r15_branch branch_check_r15/ A branch_check_r15/dir1 A branch_check_r15/dir1/file2 A branch_check_r15/file1 체크아웃된 리비전 20. [root@localhost svn_checkout]# svn checkout svn://localhost/svn_repos/branch1/r16_branch branch_check_r16/ A branch_check_r16/dir1 A branch_check_r16/dir1/file2 A branch_check_r16/file1 체크아웃된 리비전 20. [root@localhost checkout4]# svn merge svn://localhost/svn_repos/branch1/r16_branch/file1 svn://localhost/svn_repos/branch1/r15_branch/file1 ./file1 충돌 발견됨: '/home/svn_checkout/checkout4/file1'. 선택: (p) 다음에 시도, (df) 전체 비교, (e) 수정, (mc) 작업사본을 선택, (tc) 저장소 것을 선택, (s) 모든 옵션 보기:df --- /home/svn_checkout/checkout4/svn-NfMTdb 2017 1월 25 15:20:07 (수) +++ /home/svn_checkout/checkout4/.svn/tmp/file1.tmp 2017 1월 25 15:20:07 (수) @@ -1,4 +1,11 @@ -aeeee -Bffff -Cgggg -dhhhh +<<<<<<< .working +a +B +c +d +======= +aAAA +Bbbb +cCCC +dDDD +>>>>>>> .merge-right.r22 선택: (p) 다음에 시도, (df) 전체 비교, (e) 수정, (r) 해결상태로 전환, (mc) 작업사본을 선택, (tc) 저장소 것을 선택, (s) 모든 옵션 보기:e <<<<<<< .working a B c d ======= aAAA Bbbb cCCC dDDD >>>>>>> .merge-right.r22 ~
[root@localhost checkout4]# svn merge svn://localhost/svn_repos/branch1/r15_branch/file1 ./file1 충돌 발견됨: '/home/svn_checkout/checkout4/file1'. 선택: (p) 다음에 시도, (df) 전체 비교, (e) 수정, (mc) 작업사본을 선택, (tc) 저장소 것을 선택, (s) 모든 옵션 보기:e 선택: (p) 다음에 시도, (df) 전체 비교, (e) 수정, (r) 해결상태로 전환, (mc) 작업사본을 선택, (tc) 저장소 것을 선택, (s) 모든 옵션 보기:e <<<<<<< .working a B C d ======= aAAA Bbbb cCCC dDDD >>>>>>> .merge-right.r22
서로 다른 브랜치를 만들고 각자 다른 식으로 수정을 한뒤에, merge를 하니 충돌이 일어나는 것을 볼 수 있다.
필자는 심험삼아 간단한 부분들만 수정하고 했지만, 실제 사용에 따라서는, 이런 부분에서 자동으로 병합이 이루어 지거나, 충돌이 일어나면 스스로 판단하에 잘 풀어주거나 분리 시켜야 할 것이다.