사전 SCCS
구현체

SCCS

gabury1고친 사람 github-actions[bot]

SCCS 는 고친 파일을 다시 넣을 때마다 그 판을 빠짐없이 쌓아 두는 도구입니다. 누가 언제 왜 고쳤는지도 판마다 함께 남깁니다. 예전 판은 언제든 다시 꺼낼 수 있습니다. Git 같은 오늘날 버전관리 도구보다 앞선 첫 세대의 도구입니다.

쉽고 빠른 이해
  • 무슨 일을 하나 — 소스 파일 하나의 변경 이력을 보관합니다. 지난주에 고치기 전의 hello.c 가 필요하면 그 판을 꺼내 줍니다.
  • 왜 있나 — 파일을 덮어쓰면 예전 내용이 사라집니다. 누가 왜 그 줄을 바꿨는지도 같이 사라집니다.
  • 어떻게 도나
    1. 고치기 전에 파일을 잠그고 꺼냅니다. 그동안 다른 사람은 같은 판을 고치러 못 꺼냅니다
    2. 고친 뒤 다시 넣으면 1.2, 1.3 처럼 번호가 붙은 판이 하나 늡니다. 고친 이유도 함께 적습니다
    3. 모든 판은 이력 파일 하나에 섞여 들어갑니다. 꺼낼 때는 원하는 판의 줄만 골라냅니다
  • 대가 — 파일마다 이력이 따로라서 여러 파일을 한 묶음으로 넣지 못합니다. 한 사람이 잠근 동안 다른 사람은 기다려야 합니다.

상세

SCCS(Source Code Control System, 소스 코드 제어 시스템)는 파일 단위로 동작하는 버전관리 도구입니다. 버전관리는 파일이 바뀌어 온 판을 모두 남겨 두고 필요할 때 되돌리는 일입니다. 이런 도구가 없으면 누가 어느 줄을 왜 바꿨는지 알 길이 없습니다. 망가진 판을 되돌릴 방법도 없습니다.

SCCS 는 유닉스 초기에 벨 연구소에서 나왔습니다. 뒤에 나온 RCS(Revision Control System, 리비전 제어 시스템)는 SCCS 를 대신하려고 만들어졌습니다.

POSIX(Portable Operating System Interface)는 유닉스 계열 운영체제가 지킬 명령과 호출을 정한 표준입니다. SCCS 명령들은 이 표준에 선택 항목으로 들어 있습니다. 모든 유닉스가 갖출 필요는 없는 부분이라는 뜻입니다.

아래 소절은 일곱입니다. 앞의 셋은 SCCS 가 무엇을 한 단위로 보는지, 판을 어떻게 부르는지, 판을 넣고 꺼내는 명령을 봅니다. 가운데 셋은 동시 수정을 막는 잠금, 모든 판을 한 파일에 담는 방식, 파일 안에 판 번호를 채우는 키워드입니다. 마지막 소절은 SCCS 가 못 하는 것과 그 뒤를 이은 도구입니다.

파일 단위 이력

SCCS 는 파일마다 따로 이력을 둡니다. hello.c 의 이력과 util.c 의 이력은 서로 모릅니다. 프로젝트 전체를 한 덩어리로 보는 Git 과 가장 크게 다른 점이 이것입니다.

한 파일의 이력은 이름 앞에 s. 를 붙인 파일 하나에 모두 들어갑니다. hello.c 의 이력은 s.hello.c 입니다. 이 파일을 SCCS 파일이라고 부릅니다. 흔히 작업 폴더 아래 SCCS 라는 하위 폴더에 모아 둡니다.

사람이 편집기로 여는 보통 파일 hello.c 는 따로 있습니다. SCCS 는 이 파일을 g-file 이라고 부릅니다. SCCS 파일에서 한 판을 뽑아 만든 파일이라는 뜻입니다.

델타와 SID

델타는 SCCS 파일에 새로 넣은 판 하나입니다. 델타라는 말은 원래 앞 판과 뒤 판의 차이라는 뜻입니다. SCCS 는 판을 넣을 때 바뀐 차이만 기록하기 때문에 판 하나를 이 이름으로 부릅니다.

델타마다 번호가 붙습니다. 이 번호가 SID(SCCS Identification, SCCS 식별자)입니다. 처음 넣은 판이 1.1 이고 다음이 1.2, 1.3 입니다. 번호를 보면 어느 판이 먼저인지 바로 압니다. 델타 하나가 다른 도구의 리비전 하나에 해당합니다. SID 는 그 리비전의 번호입니다.

SID 는 칸마다 뜻이 있습니다. 보통은 앞의 두 칸만 씁니다. 네 칸은 브랜치 위의 판에만 붙습니다. 브랜치는 한 판에서 갈래를 쳐서 따로 고쳐 나가는 줄기입니다.

칸 이름 무엇을 가리키나 예
첫째 릴리스 사람이 크게 한 번 올리는 판 묶음 1.3 → 2.1
둘째 레벨 릴리스 안에서 델타마다 하나씩 오르는 번호 1.1 → 1.2
셋째 브랜치 한 판에서 갈라진 갈래의 번호 1.2.1.1
넷째 시퀀스 그 갈래 안에서 오르는 번호 1.2.1.2

이미 배포한 1.2 에 버그 수정만 따로 얹고 싶을 때 1.2 에서 갈래를 칩니다. 본줄기는 그동안 1.3 으로 나아갑니다. 갈래 위의 첫 판은 1.2.1.1 이 됩니다.

판을 넣고 꺼내는 명령

SCCS 는 명령 하나가 아니라 작은 명령 여럿으로 되어 있습니다. SCCS 파일을 만드는 admin, 판을 꺼내는 get, 판을 넣는 delta, 이력을 보여 주는 prs 가 중심입니다. 이 명령들을 한데 묶어 부르기 쉽게 한 sccs 명령도 있습니다.

sccs 는 파일 이름 앞에 SCCS/s. 를 붙여 해당 명령에 넘깁니다. 명령에 옵션을 붙여 부르는 흔한 조합에는 짧은 이름도 있습니다. sccs create 는 지금 있는 hello.c 를 첫 판 1.1 로 삼아 admin 으로 SCCS 파일을 만듭니다. sccs edit 는 고치려고 꺼내는 get -e 와 같습니다.

아래는 파일 하나를 만들고 한 번 고쳐 넣은 뒤 예전 판을 꺼내는 흐름입니다.

터미널
sccs create hello.c     # 1.1 로 만든다
sccs edit hello.c       # 잠그고 꺼낸다
# 편집기로 hello.c 를 고친다
sccs delta hello.c      # 1.2 로 넣는다
sccs get -r1.1 hello.c  # 1.1 을 꺼낸다

-e 는 고치려고 꺼낸다는 뜻입니다. 이때 잠금이 걸립니다. -e 없이 꺼낸 파일은 읽기 전용이라 고쳐도 다시 넣을 수 없습니다.

delta 는 판을 넣을 때 고친 이유를 묻습니다. 그 한 줄이 넣은 사람과 넣은 시각과 함께 그 델타에 붙어 남습니다. prs 로 이력을 보면 판마다 이 세 가지가 나옵니다. Git 의 커밋 메시지가 하는 일을 파일마다 하는 셈입니다.

고치는 사람을 한 명으로 묶는 잠금

get -e 로 파일을 꺼내면 SCCS 는 p-file 에 기록을 남깁니다. p-file 은 s.hello.c 옆에 생기는 p.hello.c 입니다. 누가 어느 판을 꺼냈고 넣으면 몇 번 판이 될지가 적힙니다.

p-file 에 기록이 있는 동안 같은 판을 또 get -e 로 꺼내면 SCCS 가 거절합니다. 먼저 꺼낸 사람이 delta 로 넣어야 기록이 지워지고 다음 사람이 꺼낼 수 있습니다. 이렇게 충돌이 나기 전에 한 사람만 고치게 막는 방식을 비관적 잠금이라고 합니다.

sequenceDiagram
    participant 갑
    participant 을
    participant S as SCCS 파일과 p-file
    갑->>S: get -e 로 1.2 를 꺼낸다
    Note over S: p-file 에 갑이 1.2 를 꺼냈다고 적는다
    을->>S: get -e 로 1.2 를 꺼내려 한다
    S-->>을: 거절한다
    갑->>S: delta 로 1.3 을 넣는다
    Note over S: p-file 에서 갑의 기록을 지운다
    을->>S: get -e 로 1.3 을 꺼낸다

고치다 그만두고 싶으면 unget 으로 꺼낸 것을 물립니다. p-file 의 기록이 지워지고 잠금이 풀립니다.

모든 판을 한 파일에 엮어 두는 저장 방식

SCCS 파일 안에는 어느 판에든 한 번이라도 있었던 줄이 전부 한 번씩 들어 있습니다. 줄마다 어느 델타에서 들어왔고 어느 델타에서 빠졌는지가 표시됩니다. 이 방식을 끼워 넣은 델타(interleaved deltas)라고 부릅니다. 판들을 한 파일에 엮어 둔다는 뜻에서 위브(weave)라고도 부릅니다.

어떤 판을 꺼낼 때 SCCS 는 파일을 처음부터 끝까지 한 번 훑습니다. 훑으면서 그 판에 들어 있어야 하는 줄만 골라 g-file 에 씁니다. 아래 그림은 두 판이 한 파일에 섞여 있다가 갈려 나오는 모습입니다.

flowchart TD
    subgraph F["SCCS 파일 s.hello.c"]
        A["줄 A · 1.1 에서 들어옴"]
        B["줄 B · 1.1 에서 들어옴 · 1.2 에서 빠짐"]
        C["줄 C · 1.2 에서 들어옴"]
    end
    F -->|"1.1 을 꺼낸다"| V1["줄 A · 줄 B"]
    F -->|"1.2 를 꺼낸다"| V2["줄 A · 줄 C"]

이 방식에서는 어느 판을 꺼내든 드는 수고가 같습니다. 가장 새 판이든 가장 오래된 판이든 파일 전체를 한 번 훑어야 합니다. RCS 는 이 점을 바꿨습니다. 가장 새 판을 온전한 모양으로 따로 두어, 사람들이 가장 자주 꺼내는 판이 훑지 않고 바로 나오게 했습니다.

대신 SCCS 방식은 줄마다 들어온 델타를 이미 알고 있습니다. get -m 으로 꺼내면 줄마다 그 줄을 넣은 판의 SID 가 앞에 붙어 나옵니다. 어느 줄을 누가 언제 넣었는지 한 번 훑어서 알 수 있습니다. Git 에서 git blame 이 하는 일입니다.

파일 안에 판 번호를 채우는 식별 키워드

소스 파일 안에 %I% 같은 글자를 적어 두면, get 으로 꺼낼 때 SCCS 가 그 글자를 판 번호로 바꿔 씁니다. 이런 글자를 식별 키워드라고 부릅니다. 컴파일한 프로그램이 어느 판의 소스에서 나왔는지 나중에 알아내려고 씁니다.

아래 두 줄은 1.2 판을 get 으로 꺼냈을 때 바뀌는 모습입니다. %W% 는 파일 이름과 판 번호 앞에 @(#) 라는 네 글자 표지를 붙여 줍니다. 파일 이름과 판 번호 사이는 탭입니다.

C
char v[] = "%I%";  // "1.2"
char w[] = "%W%";  // "@(#)hello.c 1.2"

what 명령은 파일에서 @(#) 표지를 찾아 그 뒤의 글자를 보여 줍니다. 컴파일된 실행 파일에도 이 문자열은 남아 있습니다. 그래서 실행 파일에 what 을 돌리면 어느 소스 파일의 몇 번 판이 들어갔는지 나옵니다.

get -e 로 고치려고 꺼낼 때는 %I% 를 판 번호로 바꾸지 않습니다. 만약 1.2 로 바꿔 꺼냈다면, 고친 파일에는 %I% 대신 1.2 가 적힌 채 다시 들어갑니다. 그러면 1.3, 1.4 를 꺼내도 그 줄은 계속 1.2 로 남습니다.

SCCS 가 못 하는 것

여러 파일을 한 묶음으로 넣지 못합니다. hello.c 와 util.c 를 같이 고쳤어도 델타는 파일마다 따로 생깁니다. 두 파일이 같은 수정에 속한다는 것은 이력 어디에도 남지 않습니다.

네트워크 너머의 저장소도 없습니다. SCCS 파일은 한 컴퓨터의 폴더에 있습니다. 여러 사람이 쓰려면 모두 같은 컴퓨터에 들어오거나 같은 파일 시스템을 나눠 써야 합니다.

잠금 방식은 사람을 기다리게 합니다. 누군가 파일을 꺼내 둔 채 퇴근하면 다른 사람은 그 판을 고칠 수 없습니다. 이 셋을 채우려고 뒤이어 RCS, CVS(Concurrent Versions System, 동시 버전 시스템), Subversion, Git 이 나왔습니다.

지금 SCCS 를 만나는 것은 대개 오래된 유닉스 코드베이스에서입니다. 폴더에 SCCS/s. 로 시작하는 파일이 있으면 그 코드는 SCCS 로 이력을 관리해 온 것입니다. 이력을 읽으려면 위의 prs 와 get 을 씁니다.

관련 항목

SCCS 를 이루는 구성 요소

SCCS 파일 · 델타 · SID · 브랜치 · p-file · g-file · 위브 · 식별 키워드

SCCS 를 부리는 명령

admin (SCCS) · get (SCCS) · delta (SCCS) · prs · unget · rmdel · sccs (명령) · what

SCCS 가 여러 사람의 수정을 다루는 방식

체크인 · 체크아웃 · 잠금 · 비관적 잠금 · 낙관적 잠금 · 병합 · 병합 충돌

SCCS 의 저장 방식과 견줘지는 기법

끼워 넣은 델타 · 역방향 델타 · 순방향 델타 · diff · git blame

SCCS 를 뒤이은 버전관리 도구

RCS · CVS · Subversion · Git · Mercurial · BitKeeper · Perforce

SCCS 가 속하는 상위 분류

버전관리 · 중앙집중식 버전관리 · 소스 코드 관리 · 유닉스 · POSIX

다른 이름: Source Code Control System · 소스 코드 제어 시스템