RCS
고친 사람 github-actions[bot]
RCS 는 파일 하나가 고쳐질 때마다 그 판을 쌓아 둡니다. 예전 판을 언제든 다시 꺼낼 수 있습니다. Git 같은 오늘날 버전관리 도구보다 앞선 세대의 도구입니다. 휴대전화 메시지 규격도 같은 이름으로 부릅니다. 이 항목은 버전관리 도구를 다룹니다.
쉽고 빠른 이해
- 무슨 일을 하나 — 파일 한 개의 변경 이력을 보관합니다. 설정 파일을 고치기 전 판으로 되돌리고 싶을 때 그 판을 꺼내 줍니다.
- 왜 있나 — 파일을 덮어쓰면 예전 내용이 사라집니다.
설정_옛날.txt같은 사본을 손으로 만들면 어느 것이 최신인지 금방 헷갈립니다. - 어떻게 도나
- 파일을 넣을 때마다 1.1, 1.2 처럼 번호가 붙은 판이 하나씩 쌓입니다
- 최신 판은 전체를 두고 예전 판은 차이만 적어 둡니다
- 고치려는 사람은 파일을 잠그고 꺼냅니다. 그동안 다른 사람은 못 넣습니다
- 대가 — 파일마다 이력이 따로라서 여러 파일을 한 묶음으로 저장하지 못합니다. 네트워크 너머의 동료와 나눠 쓰는 기능도 없습니다.
상세
RCS(Revision Control System, 리비전 제어 시스템)는 파일 단위로 동작하는 버전관리 도구입니다. 버전관리는 파일이 바뀌어 온 판을 모두 남겨 두고 필요할 때 되돌리는 일입니다. 이런 도구가 없으면 파일을 덮어쓸 때마다 예전 내용이 사라집니다. 서버 한 대의 설정 파일 하나를 고칠 때마다 판을 남겨 두는 것이 RCS 가 하는 일의 전형입니다.
RCS 는 1980년대 초 퍼듀 대학교의 Walter F. Tichy 가 만들었습니다. 그 전에는 SCCS(Source Code Control System, 소스 코드 제어 시스템)가 같은 일을 했습니다. RCS 는 SCCS 를 대신하려고 나왔습니다. 한동안 유닉스 계열에서 널리 쓰였습니다.
아래 소절은 여덟입니다. 앞의 둘은 RCS 가 무엇을 한 단위로 보고 판을 어떻게 부르는지 봅니다. 가운데 다섯은 판을 넣고 꺼내는 명령, 동시 수정을 막는 잠금, 저장 방식, 갈라진 판을 합치는 법, 파일 안에 판 번호를 채우는 키워드 치환입니다. 마지막 소절은 RCS 가 못 하는 것과 그것을 채운 뒤의 도구입니다.
파일 하나가 단위다
RCS 는 파일마다 따로 이력을 둡니다. hello.c 의 이력과 util.c 의 이력은 서로 모릅니다. 프로젝트
전체를 한 덩어리로 보는 Git 과 가장 크게 다른 점이 이것입니다.
RCS 파일과 리비전 번호
RCS 는 파일 하나의 이력을 이름 끝에 ,v 를 붙인 파일 하나에 담습니다. hello.c 의 이력은
hello.c,v 입니다. 이 파일을 RCS 파일이라고 부릅니다. 작업 폴더 안에 RCS 라는 하위 폴더가 있으면
RCS 파일은 그 안에 들어갑니다.
RCS 파일 안에 쌓이는 판 하나하나를 리비전이라고 부릅니다. 리비전은 번호로 가리킵니다. 처음 넣은 판이 1.1 이고 다음이 1.2, 1.3 입니다. 번호를 보면 어느 판이 먼저인지 바로 압니다. 이 글에서 「판」이라고 적은 것도 모두 리비전입니다.
체크인과 체크아웃
사람이 편집기로 열어 고치는 보통 파일 hello.c 를 작업 파일이라고 합니다. 작업 파일에는 지금 판
하나만 있습니다. 지나온 판들은 RCS 파일에 쌓입니다. 두 파일 사이에서 판을 옮기는 명령이 둘 있습니다.
작업 파일의 판을 RCS 파일에 넣는 일이 체크인입니다. 명령은 ci 입니다. 거꾸로 RCS 파일에서 판을
꺼내 작업 파일로 만드는 일은 체크아웃입니다. 이쪽은 co 가 맡습니다.
아래는 파일 하나를 두 번 넣고 예전 판을 꺼내는 흐름입니다.
ci -u hello.c # 리비전 1.1 로 넣는다
co -l hello.c # 잠그고 꺼낸다
ci -u hello.c # 리비전 1.2 로 넣는다
co -r1.1 hello.c # 1.1 을 다시 꺼낸다
rlog hello.c # 리비전 목록과 설명
ci 는 넣을 때마다 무엇을 바꿨는지 적을 설명을 묻습니다. rlog 가 보여 주는 것이 이 설명의
목록입니다.
옵션 없이 ci 를 부르면 넣은 뒤 작업 파일이 지워집니다. 판은 RCS 파일에 들어갔으니 잃는 것은
없습니다. 첫 줄과 셋째 줄의 -u 는 넣은 뒤에도 읽기 전용 작업 파일을 남기라는 뜻입니다.
두 번째 줄의 -l 은 잠금을 거는 옵션입니다. 잠금 없이 꺼낸 작업 파일은 읽기 전용이라 고칠 수
없습니다. 고치려면 이 옵션으로 잠그고 꺼내야 합니다.
잠금으로 동시 수정을 막는다
두 사람이 같은 파일을 동시에 고치면 한쪽 수정이 다른 쪽을 덮을 수 있습니다. RCS 는 이 일을 잠금으로 막습니다. 리비전을 잠근 사람만 그 다음 판을 넣을 수 있습니다. 새 판을 넣으면 잠금이 풀립니다.
sequenceDiagram
participant A as 개발자 A
participant R as RCS 파일
participant B as 개발자 B
A->>R: 잠그고 꺼낸다
B->>R: 잠그고 꺼내려 한다
R-->>B: A 가 잠갔다며 거절한다
A->>R: 새 리비전을 넣고 잠금을 푼다
B->>R: 다시 잠그고 꺼낸다
B 는 A 가 넣을 때까지 기다립니다. 그 대신 둘의 수정이 부딪힐 일이 없습니다. 이렇게 고치기 전에 먼저 잠그는 방식을 비관적 잠금이라고 부릅니다. 뒤에 나온 CVS(Concurrent Versions System, 동시 버전 시스템)와 Git 은 각자 고치고 나중에 합치는 쪽으로 옮겨 갔습니다.
최신 판만 온전히 두는 저장 방식
판마다 파일 전체를 저장하면 공간이 빨리 찹니다. 그래서 RCS 는 두 판의 차이만 적은 기록을 둡니다. 이런 기록을 델타라고 부릅니다. 차이는 diff 같은 줄 비교 도구가 계산합니다.
RCS 는 가장 최신 리비전만 전체 내용으로 둡니다. 예전 리비전은 바로 다음 판에서 거꾸로 되돌리는 델타로 적습니다. 이것을 역방향 델타라고 부릅니다.
flowchart TD
subgraph F["RCS 파일 hello.c,v"]
N3["리비전 1.3 · 전체 내용"]
N2["리비전 1.2 · 1.3 에서 되돌리는 델타"]
N1["리비전 1.1 · 1.2 에서 되돌리는 델타"]
end
N3 -->|"델타를 적용한다"| N2
N2 -->|"델타를 적용한다"| N1
최신 판을 꺼낼 때는 델타를 하나도 적용하지 않습니다. 1.1 을 꺼내려면 1.3 에서 출발해 델타 둘을 차례로 적용합니다. 사람들이 가장 자주 꺼내는 판이 최신 판이라서 이렇게 정했습니다.
모든 판을 파일 하나에 담는 것은 SCCS 도 같습니다. 두 도구가 갈리는 것은 판들이 그 파일 안에 놓이는 모양입니다.
SCCS 는 모든 판의 줄을 한 줄기로 섞어 둡니다. 줄마다 그 줄이 들어온 판과 빠진 판이 표시됩니다. 그래서 어느 판을 꺼내든 파일 전체를 처음부터 끝까지 훑어야 합니다. 그 판에 속한 줄만 골라내야 하기 때문입니다.
RCS 는 이 방식 대신 최신 판을 온전한 모양으로 따로 두었습니다. 그래서 최신 판은 파일 전체를 훑지 않고 바로 나옵니다.
브랜치와 병합
한 리비전에서 갈래를 쳐서 따로 고쳐 나갈 수도 있습니다. 이 갈래를 브랜치라고 부릅니다. 예를 들어 이미 배포한 1.2 에 버그 수정만 따로 얹고 싶을 때 1.2 에서 브랜치를 칩니다. 본줄기는 그동안 1.3 으로 나아갑니다.
브랜치 위의 리비전은 번호가 네 칸입니다. 1.2 에서 친 첫 브랜치의 첫 판이 1.2.1.1 이고 다음이 1.2.1.2 입니다.
flowchart TD
A["1.1"] --> B["1.2"]
B --> C["1.3 · 본줄기"]
B --> D["1.2.1.1 · 브랜치"]
D --> E["1.2.1.2"]
브랜치 위의 판은 역방향이 아니라 순방향 델타로 적힙니다. 순방향 델타는 앞 판에 더해 다음 판을 만드는 차이입니다. 1.2.1.1 은 1.2 에 더하는 델타입니다. 1.2.1.2 는 다시 1.2.1.1 에 더하는 델타입니다.
그래서 1.2.1.2 를 꺼내는 길은 두 단계입니다. 먼저 최신 판 1.3 에서 역방향 델타를 적용해 1.2 를 되살립니다. 거기에 순방향 델타 둘을 차례로 더해 1.2.1.2 에 이릅니다. 브랜치 끝의 판이 본줄기 최신 판보다 꺼내는 데 오래 걸리는 까닭입니다.
두 갈래의 수정을 하나로 합치는 일을 병합이라고 부릅니다. 위 그림이면 브랜치 1.2.1.2 에 얹은 버그
수정을 본줄기 1.3 에도 옮겨 담는 일입니다. RCS 에서는 rcsmerge 가 이 일을 맡습니다.
1.3 과 1.2.1.2 를 합칠 때 기준이 되는 판은 두 갈래가 갈라진 1.2 입니다. 이런 판이 공통 조상입니다. 공통 조상과 견주면 어느 갈래가 어느 줄을 바꿨는지 알 수 있습니다.
한쪽 갈래만 고친 줄은 그 수정을 받아들입니다. 두 판과 공통 조상, 이렇게 셋을 함께 보고 합치는 방식을 세 갈래 병합이라고 합니다. 두 판만 견주면 어느 쪽이 그 줄을 고쳤는지 가릴 수 없기 때문입니다.
양쪽 갈래가 같은 줄을 서로 다르게 고쳤으면 어느 쪽을 받을지 기계가 정할 수 없습니다. 이것이
병합 충돌입니다. 충돌한 부분은 <<<<<<< · ======= · >>>>>>> 세 표식으로 감싸여 파일에 들어갑니다.
사람이 이 표식 사이를 보고 어느 쪽을 남길지 고릅니다. Git 도 이 표식을 물려받았습니다. 표식을 걷고 한쪽을 고르는 일은 충돌 해소에서 다룹니다.
키워드 치환
파일 안에 $Id$ 나 $Revision$ 같은 키워드를 적어 둘 수 있습니다. 꺼낼 때 RCS 가 이 키워드를
리비전 번호, 날짜, 작성자 같은 값으로 채웁니다. 이것을 키워드 치환이라고 부릅니다.
키워드가 있으면 파일만 보고도 어느 리비전인지 압니다. 이력 도구가 곁에 없는 서버에 복사된 파일도 몇 번째 판인지 알아낼 수 있습니다.
못 하는 것과 뒤를 이은 도구
RCS 는 파일마다 리비전 번호가 따로 올라갑니다. hello.c 는 1.5 인데 util.c 는 1.3 일 수 있습니다.
두 파일을 함께 고쳐도 그 둘을 한 번의 변경으로 묶어 넣지 못합니다. 리비전마다 태그 같은
이름표를 붙여 묶을 수는 있습니다. 그 일은 사람이 손으로 해야 합니다.
네트워크 기능도 없습니다. RCS 파일은 한 컴퓨터의 폴더에 있습니다. 여러 사람이 쓰려면 모두 같은 컴퓨터에 들어오거나 같은 파일 시스템을 나눠 써야 합니다.
이 둘을 채우려고 CVS 가 나왔습니다. CVS 는 RCS 파일 형식을 물려받았습니다. 그 위에 여러 파일을 한 프로젝트로 다루는 기능과 네트워크 너머의 저장소를 얹었습니다. 그 뒤를 Subversion 과 Git 이 이었습니다.
지금도 RCS 가 맞는 경우가 있습니다. 서버 한 대에서 설정 파일 몇 개만 이력을 남기고 싶을 때입니다. 저장소를 따로 만들 필요 없이 파일 옆에 RCS 파일 하나만 생깁니다. 여러 사람이 여러 파일을 함께 고치는 프로젝트는 변경을 묶어 넣지 못하는 한계에 바로 걸립니다.
관련 항목
RCS 를 이루는 구성 요소
RCS 파일 · 리비전 · 델타 · 역방향 델타 · 브랜치 · 태그 · 키워드 치환
RCS 를 부리는 명령
ci (인터페이스) · co · rlog · rcsdiff · rcsmerge · ident
RCS 가 여러 사람의 수정을 다루는 방식
체크인 · 체크아웃 · 잠금 · 비관적 잠금 · 낙관적 잠금 · 병합 · 세 갈래 병합 · 공통 조상 · 병합 충돌 · 충돌 해소
RCS 가 차이를 계산할 때 기대는 도구
diff · diff3 · 패치 · 최장 공통 부분 수열
RCS 를 앞서거나 뒤이은 버전관리 도구
SCCS · CVS · Subversion · Git · Mercurial · Perforce
RCS 가 속하는 상위 분류
다른 이름: Revision Control System · 리비전 제어 시스템