Subversion
고친 사람 github-actions[bot]
Subversion 은 팀이 함께 고치는 파일의 이력을 서버 한 곳에 모아 관리해 주는 도구입니다. 모두가 같은 서버에서 파일을 받아 고칩니다. 고친 것은 다시 그 서버에 올립니다. 파일과 이력을 담아 두는 서버 쪽 보관소를 저장소라고 합니다. 올릴 때마다 파일 하나가 아니라 저장소 전체에 번호가 하나씩 붙습니다. 줄여서 SVN 이라고 부릅니다.
쉽고 빠른 이해
Subversion 은 팀의 파일과 그 변경 기록을 서버 하나에 모아 두는 도구입니다. 서버에서 이 기록을 담아 두는 보관소를 저장소라고 부릅니다. 열 명이 한 프로젝트를 고치면 열 명 모두 그 서버에서 파일을 받습니다. 고친 것도 그 서버에 올립니다.
기록이 서버 한 곳에만 있으니 「지금 최신이 무엇인가」가 늘 분명합니다. 누가 어느 폴더를 볼 수 있는지도 그 서버 하나에서 정합니다.
어떻게 도나:
- 서버에서 파일 한 벌을 받아 내 컴퓨터에서 고칩니다
- 올리기 전에 남이 먼저 올린 변경을 받아 내 것과 합칩니다
- 올리면 서버가 파일 하나가 아니라 저장소 전체에 새 번호를 하나 붙입니다
대가가 있습니다. 서버에 연결되지 않으면 지난 기록을 볼 수도, 새로 올릴 수도 없습니다. 올리는 순간 모두에게 보이기도 합니다. 그래서 서버에 올리기 전에 나만 보는 저장을 여러 번 쌓아 두기 어렵습니다.
그래도 큰 그림·음원 파일을 코드와 함께 다루는 팀은 지금도 Subversion 을 고릅니다. 폴더마다 볼 사람을 나누거나, 파일을 잠가 한 사람만 고치게 해야 하는 팀도 그렇습니다.
상세
Subversion 은 버전관리 도구입니다. 버전관리는 파일이 언제 어떻게 바뀌었는지를 남겨 두고 예전 모습으로 되돌릴 수 있게 하는 일입니다. 정식 이름은 Apache Subversion 입니다. Apache 소프트웨어 재단이 만들고 있습니다. 서버에 설치해 저장소를 띄웁니다. 쓰는 사람은 각자 컴퓨터에 명령줄 도구 svn 을 깝니다.
이 절은 이력을 서버 한 곳에 두는 방식부터 봅니다. 그다음 작업 사본, 리비전 번호, 하루 작업 흐름, 충돌과 파일 잠금을 봅니다. 끝으로 브랜치, 서버에 닿는 방법, 이 도구가 포기한 것, Git 명령과의 짝을 봅니다.
이력을 서버 한 곳에 두는 방식
버전관리 도구는 이력을 어디에 두느냐로 갈립니다. 이력을 서버 한 곳에만 두는 방식을 중앙집중식 버전관리라고 합니다. Subversion 이 이쪽입니다. 반대편은 사람마다 이력 전체를 한 벌씩 가지는 분산 버전관리입니다. Git 이 그쪽입니다.
Subversion 은 그 전에 널리 쓰이던 중앙집중식 도구 CVS(Concurrent Versions System)를 대신하려고 만들었습니다. Subversion 은 CVS 의 방식을 그대로 살렸습니다. 이력은 서버 한 곳에 두고, 각자 복사해 고친 뒤 합치는 방식입니다. 그러면서 버그와 불편한 동작은 걷어 냈습니다.
파일과 그 이력 전체를 담은 보관소를 저장소라고 부릅니다. Subversion 에서 저장소는 서버에 하나만 있습니다. 이 글에서는 이것을 중앙 저장소라고 적습니다. 무엇이 최신이고 무엇이 정본인지는 언제나 중앙 저장소가 정합니다.
개발자는 저마다 자기 컴퓨터에 파일의 최신 모습만 받아 둡니다. 이 사본을 작업 사본이라고 부릅니다.
flowchart TD
S["중앙 저장소 · 서버 · 이력 전체"]
S -->|받기| A["작업 사본 · 개발자 A"]
S -->|받기| B["작업 사본 · 개발자 B"]
S -->|받기| C["작업 사본 · 개발자 C"]
A -.->|올리기| S
그림에서 이력 전체를 가진 것은 맨 위 하나뿐입니다. 아래 세 작업 사본은 최신 모습만 받아 옵니다. 고친 것은 점선처럼 다시 위로 올립니다.
작업 사본
중앙 저장소에서 파일을 처음 받아 오는 일을 체크아웃이라고 합니다. 체크아웃하면 내 컴퓨터에 파일 한 벌이 생깁니다. 이 한 벌이 앞에서 말한 작업 사본입니다. 고치는 일은 전부 작업 사본에서 합니다.
작업 사본 안에는 .svn 이라는 숨은 폴더가 함께 생깁니다. 여기에 받아 온 시점의 원본이 한 벌 더 들어 있습니다. 그래서 서버에 연결되지 않아도 「내가 무엇을 고쳤나」를 볼 수 있습니다. 고친 것을 원래대로 되돌리는 일도 됩니다.
지난 이력은 .svn 에 들어 있지 않습니다. 누가 언제 무엇을 바꿨는지 보려면 서버에 물어야 합니다. 고친 것을 올릴 때도 서버가 있어야 합니다.
저장소의 일부만 받아 올 수도 있습니다. 저장소 안의 폴더 하나를 골라 그 아래만 체크아웃하면 됩니다. 저장소가 아주 커도 내 컴퓨터에는 그 폴더만 생깁니다.
저장소 전체에 붙는 리비전 번호
고친 것을 중앙 저장소에 올려 새 이력으로 남기는 일을 커밋이라고 합니다. 커밋해야 남도 내 변경을 받아 갈 수 있습니다.
중앙 저장소는 커밋을 하나 받을 때마다 번호를 1 올립니다. 이 번호가 리비전입니다. 갓 만든 빈 저장소가 리비전 0 입니다. 첫 커밋이 리비전 1 이 됩니다.
번호는 파일 하나가 아니라 저장소 전체에 붙습니다. 리비전 58 은 「58번째 커밋을 받은 직후 저장소 전체의 모습」입니다. 그 커밋이 파일 하나만 고쳤어도 저장소 전체가 58 이 됩니다. 그래서 어떤 파일의 리비전 57 과 58 은 내용이 같을 수 있습니다.
번호가 순서대로 늘어나니 크기만 견줘도 앞뒤를 압니다. 「58 에서 고친 버그가 지금 배포한 60 에 들어 있나」가 숫자만 보고 바로 풀립니다.
커밋은 원자적입니다. 파일 열 개를 한 번에 올리면 열 개가 모두 들어가거나 하나도 안 들어갑니다. 올리는 도중에 연결이 끊겨도 반만 들어간 리비전은 생기지 않습니다. CVS 는 파일마다 따로 기록해서 이것이 안 됐습니다.
하루 작업 흐름
아래는 체크아웃부터 커밋까지 한 번 도는 모습입니다. $repo 에는 중앙 저장소 주소가 들어 있다고 합시다. 오른쪽 주석이 그 명령이 알려 주는 결과입니다.
svn checkout $repo calc # 리비전 56 을 받음
cd calc
# 여기서 button.c 를 고친다
svn status # M button.c
svn update # 리비전 57 로 올라감
svn commit -m 버튼수정 # 리비전 58 이 생김
첫 줄이 체크아웃입니다. calc 폴더에 작업 사본이 생깁니다. 받은 시점은 리비전 56 입니다. 그다음 편집기로 button.c 를 고쳤습니다.
svn status 는 작업 사본에서 무엇이 바뀌었나를 보여 줍니다. 앞의 M 은 고친 파일이라는 표시입니다. 이 명령은 .svn 의 원본과 견주므로 서버 없이 돕니다.
svn update 는 내가 받은 뒤에 남들이 올린 변경을 작업 사본으로 가져옵니다. 그사이 누가 하나 올렸으니 57 로 올라갔습니다. 마지막으로 svn commit 이 내 변경을 올립니다. 중앙 저장소는 리비전 58 을 만듭니다.
커밋 거절과 충돌
Subversion 은 기본으로 파일을 잠그지 않습니다. 여럿이 같은 파일을 동시에 고치게 둡니다. 각자 복사해 고치고 나중에 합치는 방식입니다.
그러면 두 사람이 같은 파일을 고치는 일이 생깁니다. 먼저 올린 사람은 그대로 커밋됩니다. 나중에 올리는 사람은 거절당합니다. 내가 고친 파일이 서버에서 이미 더 새로워졌기 때문입니다. 이 상태를 작업 사본이 낡았다(out of date)고 합니다.
sequenceDiagram
participant 나
participant 저장소 as 중앙 저장소
participant 동료
동료->>저장소: button.c 커밋
Note over 저장소: 리비전 57
나->>저장소: button.c 커밋
저장소-->>나: 거절 · 작업 사본이 낡았다
나->>저장소: svn update
저장소-->>나: 57 의 변경
Note over 나: 내 변경과 합친다
나->>저장소: 다시 커밋
Note over 저장소: 리비전 58
거절당하면 svn update 로 남의 변경을 받아 내 작업 사본에 합칩니다. 두 사람이 서로 다른 줄을 고쳤으면 병합이 저절로 끝납니다. 합친 결과를 확인하고 다시 커밋하면 됩니다.
두 사람이 같은 줄을 고쳤으면 충돌이 납니다. Subversion 은 그 파일에 양쪽 내용을 표시해 두고 멈춥니다. 사람이 파일을 열어 어느 쪽을 살릴지 고칩니다. 그다음 svn resolve 로 다 고쳤다고 알리고 커밋합니다.
파일 잠금
그림 파일이나 압축 파일 같은 이진 파일은 줄 단위로 합칠 수 없습니다. 두 사람이 같은 그림을 고치면 한쪽 작업을 버려야 합니다. 그래서 이런 파일은 고치기 전에 막는 편이 낫습니다.
svn lock 은 파일 하나를 내 몫으로 잠급니다. 잠긴 동안에는 다른 사람이 그 파일을 커밋하지 못합니다. 내가 커밋하거나 svn unlock 으로 풀면 잠금이 사라집니다. 먼저 잠그고 고치는 이 방식은 비관적 잠금과 같은 생각입니다.
Subversion 은 파일과 폴더에 이름과 값을 꼬리표처럼 붙일 수 있습니다. 이 꼬리표를 속성이라고 부릅니다. 도구가 그 파일을 어떻게 다룰지 알려 주는 데 씁니다.
잠금을 잊지 않게 하는 속성도 있습니다. 파일에 svn:needs-lock 속성을 붙이면 작업 사본에 읽기 전용으로 받아집니다. 잠가야 쓰기가 풀리므로 고치기 전에 잠그게 됩니다.
브랜치와 태그는 폴더 복사
브랜치는 이력의 줄기를 갈라 따로 고치는 일입니다. 새 기능을 만드는 동안 본 줄기를 건드리지 않으려고 씁니다. Subversion 에는 브랜치만을 위한 기능이 따로 없습니다. 저장소 안에서 폴더를 복사하면 그것이 브랜치입니다.
그래서 저장소를 관례로 세 폴더로 나눕니다. 본 줄기는 trunk 에 둡니다. 갈라진 줄기는 branches 아래에, 이름을 붙여 고정해 둘 시점은 tags 아래에 둡니다.
flowchart TD
R["저장소 맨 위"]
R --> T["trunk · 본 줄기"]
R --> B["branches"]
R --> G["tags"]
B --> B1["feature-a · trunk 를 복사"]
G --> G1["1.0 · trunk 를 복사"]
복사는 svn copy 로 합니다. 이것도 커밋 하나라 새 리비전이 생깁니다. 복사해도 파일 내용을 새로 쓰지 않습니다. 「이 폴더는 리비전 N 의 trunk 에서 왔다」는 기록만 남깁니다. 그래서 저장소가 커도 브랜치는 금방 만들어집니다.
태그도 같은 복사입니다. 「릴리스 1.0 은 이 모습이었다」를 남기려고 tags/1.0 으로 복사합니다. 태그 폴더에 커밋하는 일을 도구가 막지는 않습니다. 고치지 않는다는 것은 팀의 약속입니다.
작업 사본을 다른 브랜치로 옮길 때는 svn switch 를 씁니다. 새로 체크아웃하지 않고 지금 폴더의 내용을 그 브랜치의 모습으로 바꿉니다.
브랜치에서 고친 것을 trunk 로 가져올 때는 svn merge 를 씁니다. 병합하면 어느 리비전까지 가져왔는지를 적은 꼬리표가 붙습니다. svn:mergeinfo 라는 속성입니다. 그래서 다음 병합이 같은 변경을 두 번 가져오지 않습니다.
서버에 닿는 방법
svn 은 주소의 앞머리를 보고 서버에 닿는 방법을 고릅니다. 흔히 쓰는 것은 넷입니다.
| 주소 앞머리 | 무엇으로 닿나 |
|---|---|
svn:// |
Subversion 전용 서버 프로그램 svnserve |
svn+ssh:// |
SSH(Secure Shell)로 접속해 svnserve 를 띄움 |
https:// |
HTTP(HyperText Transfer Protocol) · Apache HTTP Server 에 Subversion 모듈을 얹은 웹 서버 |
file:// |
같은 컴퓨터의 저장소 폴더를 바로 엶 |
어느 방법이든 서버 쪽에서 폴더마다 권한을 줄 수 있습니다. 「/design 은 디자이너만 쓰고, /server 는 백엔드 팀만 읽는다」처럼 경로별로 나눕니다. 저장소가 하나라서 이 규칙도 한 곳에 모입니다.
이력을 서버에 맡긴 대가
Subversion 은 이력을 사람마다 나눠 주는 일을 포기했습니다. 정본이 하나라는 단순함을 얻는 대신 내 컴퓨터에서 할 수 있는 일이 줄었습니다.
| 포기한 것 | 그래서 생기는 일 |
|---|---|
| 내 컴퓨터의 이력 | 서버에 연결할 수 없으면 지난 기록 보기도 커밋도 못 합니다 |
| 나만 보는 커밋 | 커밋하는 순간 모두에게 보입니다. 작게 쌓았다가 다듬어 올리려면 서버에 브랜치를 따로 만들어야 합니다 |
| 사본마다 있는 백업 | 이력 전체가 서버 한 곳에만 있어서 그 서버를 따로 백업해야 합니다 |
같은 결정으로 얻은 것도 있습니다. 작업 사본에는 이력이 없습니다. 그래서 큰 이진 파일이 수백 번 바뀌어도 내 디스크에는 최신본만 쌓입니다. 폴더 하나만 받는 일, 폴더별 권한, 파일 잠금도 정본이 한 곳이라 가능합니다. 큰 그림·음원 파일을 코드와 함께 다루는 팀이 아직 Subversion 을 고르는 까닭입니다.
Git 명령과의 짝
Git 을 먼저 아는 사람은 명령을 짝지어 보면 빨리 익힙니다. 같은 낱말이 두 도구에서 다른 일을 하는 경우가 있어 특히 그렇습니다.
| 하려는 일 | Subversion | Git |
|---|---|---|
| 처음 받아 오기 | svn checkout |
git clone |
| 남의 변경 받기 | svn update |
git pull |
| 올리기 | svn commit 한 번 |
git commit 뒤에 git push |
| 다른 브랜치로 옮기기 | svn switch |
git switch 또는 git checkout |
| 한 시점을 가리키는 이름 | 순번 · 58 | 해시 · a1b2c3d |
표에서 체크아웃이 두 번 나옵니다. Subversion 의 체크아웃은 Git 의 클론에 가깝습니다. Git 의 체크아웃은 Subversion 의 switch 에 가깝습니다.
커밋도 뜻이 다릅니다. Subversion 의 커밋은 곧바로 서버에 올라갑니다. Git 의 커밋은 내 저장소에만 남습니다. 푸시해야 서버에 올라갑니다.
두 도구를 잇는 다리도 있습니다. Git 에 딸린 git svn 명령을 쓰면 Subversion 저장소를 Git 저장소처럼 받아 작업할 수 있습니다. Subversion 에서 Git 으로 옮겨 가는 팀이 이 명령으로 이력을 가져갑니다.
관련 항목
Subversion 을 떠받치는 개념
버전관리 · 중앙집중식 버전관리 · 저장소 · 리비전 · 커밋 · 원자성
Subversion 으로 하는 작업
체크아웃 · 작업 사본 · 브랜치 · 태그 · 병합 · 충돌 · 트렁크 기반 개발
Subversion 이 동시 수정을 다루는 방식
비관적 잠금 · 낙관적 잠금 · 이진 파일 · 충돌 해소
Subversion 과 겨루는 버전관리 도구
Git · Mercurial · Perforce · CVS · RCS · SCCS · 분산 버전관리
Subversion 서버를 띄우는 구성 요소
svnserve · Apache HTTP Server · SSH · WebDAV · Apache 소프트웨어 재단
Subversion 명령과 짝을 이루는 Git 개념
다른 이름: SVN · Apache Subversion · 서브버전