사전 Subversion
구현체

Subversion

gabury1고친 사람 github-actions[bot]

Subversion 은 팀이 함께 고치는 파일의 이력을 서버 한 곳에 모아 관리해 주는 도구입니다. 모두가 같은 서버에서 파일을 받아 고칩니다. 고친 것은 다시 그 서버에 올립니다. 파일과 이력을 담아 두는 서버 쪽 보관소를 저장소라고 합니다. 올릴 때마다 파일 하나가 아니라 저장소 전체에 번호가 하나씩 붙습니다. 줄여서 SVN 이라고 부릅니다.

쉽고 빠른 이해

Subversion 은 팀의 파일과 그 변경 기록을 서버 하나에 모아 두는 도구입니다. 서버에서 이 기록을 담아 두는 보관소를 저장소라고 부릅니다. 열 명이 한 프로젝트를 고치면 열 명 모두 그 서버에서 파일을 받습니다. 고친 것도 그 서버에 올립니다.

기록이 서버 한 곳에만 있으니 「지금 최신이 무엇인가」가 늘 분명합니다. 누가 어느 폴더를 볼 수 있는지도 그 서버 하나에서 정합니다.

어떻게 도나:

  1. 서버에서 파일 한 벌을 받아 내 컴퓨터에서 고칩니다
  2. 올리기 전에 남이 먼저 올린 변경을 받아 내 것과 합칩니다
  3. 올리면 서버가 파일 하나가 아니라 저장소 전체에 새 번호를 하나 붙입니다

대가가 있습니다. 서버에 연결되지 않으면 지난 기록을 볼 수도, 새로 올릴 수도 없습니다. 올리는 순간 모두에게 보이기도 합니다. 그래서 서버에 올리기 전에 나만 보는 저장을 여러 번 쌓아 두기 어렵습니다.

그래도 큰 그림·음원 파일을 코드와 함께 다루는 팀은 지금도 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 개념

git-svn · 클론 · 푸시 · 풀 · 해시

다른 이름: SVN · Apache Subversion · 서브버전