사전 리비전 이력
개념

리비전 이력

gabury1고친 사람 github-actions[bot]

리비전 이력은 데이터가 바뀌어 온 길을 처음부터 되짚게 해 줍니다. 바뀔 때마다 생긴 모습을 버리지 않고 순서대로 이어 둡니다. 덕분에 예전 모습으로 돌아가거나 언제 무엇이 바뀌었는지 찾아볼 수 있습니다.

쉽고 빠른 이해

리비전 이력은 데이터가 바뀔 때마다 생긴 모습을 순서대로 이어 둔 기록입니다. 코드 저장소의 커밋 목록이 대표입니다.

이게 없으면 지금 모습만 남습니다. 잘못 바꿨을 때 돌아갈 곳이 없습니다. 언제 누가 왜 바꿨는지 물어볼 데도 없습니다.

어떻게 도나:

  1. 바꿀 때 옛 모습을 지우지 않고 새 모습을 하나 더 만듭니다
  2. 새 모습에 「어느 모습에서 나왔는지」를 적어 둡니다
  3. 이 연결을 거꾸로 따라가면 처음 모습까지 거슬러 올라갑니다

모습이 쌓이는 만큼 저장 공간을 먹습니다. 그래서 오래된 것은 한도를 정해 지우기도 합니다. 지운 모습으로는 돌아갈 수 없습니다.

한 번 쓰고 바뀌지 않는 데이터에는 이력을 두지 않습니다. 지난 모습을 다시 볼 일이 없기 때문입니다.

상세

이 절은 통장 거래 내역에 빗대어 리비전 이력을 떠올리는 데서 시작합니다. 이력이 어떻게 이어지고 갈라지는지, 이력으로 무엇을 하는지, 얼마나 남기는지를 차례로 봅니다.

통장 거래 내역으로 떠올리기

은행 통장을 펴면 거래가 날짜순으로 한 줄씩 적혀 있습니다. 줄마다 그 거래 뒤의 잔액이 함께 찍혀 있습니다. 지난달 어느 날의 잔액이 궁금하면 그날의 줄을 찾으면 됩니다.

잘못 들어온 돈이 있어도 은행은 그 줄을 지우지 않습니다. 그 돈을 빼 가는 취소 거래를 한 줄 더 적습니다.

리비전 이력도 이 통장과 같은 모양입니다. 데이터를 한 번 바꾸는 일이 거래 한 줄입니다. 그 줄의 잔액이 바꾼 뒤의 데이터 모습입니다.

리비전 이력이 담는 것

리비전은 계속 바뀌는 데이터의 한 시점 모습에 붙인 이름입니다. 데이터를 바꿀 때마다 새 리비전이 하나 생깁니다. 리비전 이력은 이 리비전들을 만들어진 순서대로 이어 놓은 기록입니다.

설정 파일 하나를 세 번 고쳤다고 해 봅시다. 처음 만든 모습까지 치면 리비전이 넷입니다. 이력은 이 넷을 1번부터 4번까지 차례로 늘어놓은 목록입니다.

리비전에는 내용 말고도 곁에 붙는 정보가 있습니다. 누가 바꿨는지, 언제 바꿨는지, 왜 바꿨는지를 적은 짧은 메모입니다. 이렇게 데이터를 설명하는 곁 정보가 메타데이터입니다.

아래 표가 방금 든 설정 파일의 이력입니다. 한 줄이 리비전 하나입니다. 맨 아래가 가장 최근입니다.

리비전 바꾼 사람 메모
1 개발자 A 처음 만듦
2 개발자 B 접속 제한 시간을 늘림
3 개발자 A 로그를 더 자세히 남김
4 개발자 B 3번 변경을 취소함

표를 읽으면 4번이 지금 모습이라는 것을 압니다. 3번의 변경이 4번에서 취소됐다는 것도 압니다. 파일 내용만 봐서는 이 두 가지가 드러나지 않습니다.

부모를 가리켜 잇는다

리비전은 저마다 자기가 어느 리비전에서 나왔는지를 기억합니다. 이 바로 앞 리비전이 그 리비전의 부모입니다. 앞 표에서 4번의 부모는 3번입니다. 3번의 부모는 2번입니다.

가장 최근 리비전에서 부모를 차례로 따라가면 처음 리비전에 닿습니다. 이력을 읽는다는 것은 이 연결을 거꾸로 밟아 가는 일입니다. 리비전마다 부모만 적어 두면 이력 전체를 따로 적지 않아도 됩니다.

번호 순서만으로는 모자랄 때가 있습니다. 한 사람이 한 줄로 고칠 때는 번호로도 충분합니다. 여럿이 같은 리비전에서 따로 고치기 시작하면 번호로는 누가 어디서 갈라져 나왔는지 알 수 없습니다. 부모를 적어 두는 까닭이 이것입니다.

한 줄 이력과 갈라지는 이력

모든 리비전이 부모 하나, 자식 하나를 가지면 이력은 한 줄로 이어집니다. 앞의 설정 파일이 그런 이력입니다.

두 사람이 같은 리비전에서 각자 고치기 시작하면 이력이 두 갈래로 나뉩니다. 이렇게 갈라져 따로 리비전을 쌓아 가는 갈래가 브랜치입니다.

나중에 두 갈래의 변경을 하나로 합칠 수 있습니다. 이 일을 병합이라고 부릅니다. 병합으로 생긴 리비전은 부모가 둘입니다. 두 갈래를 모두 이어받았기 때문입니다. 코드 버전 관리에서는 이 리비전을 병합 커밋이라고 합니다.

아래 그림은 한 번 갈라졌다가 다시 합쳐진 이력입니다. 화살표는 「이 리비전에서 저 리비전이 나왔다」를 뜻합니다.

flowchart TD
    R1["리비전 1"] --> R2["리비전 2"]
    R2 --> R3["리비전 3 · 갈래 A"]
    R2 --> R4["리비전 4 · 갈래 B"]
    R3 --> R5["리비전 5 · 병합"]
    R4 --> R5

리비전 5는 부모가 3과 4 둘입니다. 5에서 부모를 따라 올라가면 길이 둘로 나뉘었다가 2에서 다시 만납니다.

갈라지는 이력은 한 줄이 아니라 그물 모양입니다. 그래도 화살표가 늘 과거에서 미래로만 향하므로, 따라가다 제자리로 돌아오는 고리는 생기지 않습니다. 이런 모양을 DAG(Directed Acyclic Graph, 방향 비순환 그래프)라고 부릅니다.

이력으로 하는 일

이력을 남겨 두면 지금 모습만으로는 답할 수 없는 물음에 답할 수 있습니다. 가장 흔한 것은 예전 리비전의 모습으로 돌아가는 일입니다. 이 일을 흔히 롤백이라고 부릅니다.

두 리비전을 견주는 일도 흔합니다. 두 리비전 사이에 바뀐 줄만 뽑은 것이 diff(차이)입니다. 아래 표는 이력으로 하는 일 넷을 모은 것입니다.

하는 일 무엇을 하나 이력이 없으면
롤백 문제가 생기면 예전 리비전의 모습으로 돌아간다 돌아갈 모습이 없다
diff 두 리비전 사이에 바뀐 줄을 뽑는다 무엇이 바뀌었는지 기억에 기댄다
원인 좁히기 문제가 처음 나타난 리비전을 앞뒤로 찾아 들어간다 지금 모습 하나로 원인을 추측한다
변경 추적 누가 언제 왜 바꿨는지 메타데이터로 확인한다 물어볼 기록이 없다

표의 넷은 모두 두 시점 이상을 견주는 일입니다. 리비전이 하나뿐이면 어느 것도 할 수 없습니다.

취소할 때도 이력은 지우지 않는다

이력은 뒤에 덧붙이기만 하는 것이 원칙입니다. 3번의 변경이 잘못이었다면 3번을 지우지 않습니다. 3번을 취소하는 내용으로 4번을 새로 만듭니다. 통장에서 잘못 들어온 돈을 취소 거래 한 줄로 바로잡는 것과 같습니다.

앞의 표에서 4번의 메모가 「3번 변경을 취소함」인 까닭이 이것입니다. 이렇게 하면 3번에서 무엇을 했다가 왜 취소했는지까지 남습니다.

이력은 한 곳에만 있지 않을 때가 많습니다. Git 같은 버전 관리 도구는 이력을 통째로 복사해 여러 사람이 각자 가져갑니다. 이 사본에도 1번부터 3번까지가 똑같이 있습니다. 4번을 덧붙이는 방식이면 사본과 어긋나지 않습니다. 사본 뒤에 4번이 하나 붙을 뿐입니다.

이처럼 새 리비전을 덧붙여 앞의 변경을 취소하는 방식이 되돌리기입니다. 지운 리비전이 없으니 이력은 여전히 뒤로만 자랍니다.

롤백과 되돌리기는 가리키는 것이 다릅니다. 롤백은 예전 모습으로 돌아가는 일 자체입니다. 되돌리기는 그 일을 새 리비전을 덧붙여서 하는 방식입니다.

반대로 이미 있는 이력을 고쳐 쓰는 일도 있습니다. 그 하나가 리비전 여러 개를 새 리비전 하나로 뭉치는 스쿼시입니다. 자잘한 고침 여럿을 뜻 있는 변경 하나로 정리할 때 씁니다.

다른 하나는 리베이스입니다. 한 갈래에 쌓인 리비전들을 떼어 다른 리비전 위로 옮겨 다시 쌓는 일입니다. 내 갈래를 시작한 뒤 다른 갈래에 새 리비전이 생겼을 때, 내 갈래를 그 새 리비전 뒤로 옮겨 붙이는 식입니다.

앞에서 본 대로 리비전은 부모를 기억합니다. 부모가 바뀌면 같은 변경이라도 다른 리비전이 됩니다. 리베이스는 옮긴 리비전마다 부모를 바꿉니다. 스쿼시는 여러 리비전 대신 새 리비전 하나를 만듭니다.

결국 고쳐 쓴 이력은 옛 이력과 다른 리비전들로 이루어집니다. 옛 이력을 이미 받아 간 사람의 사본과 어긋나게 됩니다. 이력 고쳐 쓰기는 남과 나누기 전에만 하는 것이 보통입니다.

얼마나 남기나

리비전이 쌓일수록 저장 공간이 듭니다. 시스템마다 이력을 얼마나 남길지 정합니다. 크게 전부 남기는 방식과 한도를 두는 방식이 있습니다.

코드처럼 먼 과거까지 되짚어야 하는 데이터는 이력을 전부 남깁니다. 리비전마다 전체를 새로 저장하지 않고 바뀐 부분만 저장해 공간을 아끼기도 합니다.

롤백용으로만 이력을 쓰는 곳은 최근 몇 개만 남깁니다. 한도를 넘은 오래된 리비전은 지웁니다. 키-값 저장소 etcd 는 오래된 리비전을 지워 공간을 되찾는 작업을 컴팩션이라고 부릅니다.

지운 리비전으로는 돌아갈 수 없습니다. 그래서 한도를 정할 때는 얼마나 먼 과거까지 돌아갈 수 있어야 하는지와 저장 공간을 함께 따집니다.

한 번 쓰고 바뀌지 않는 데이터에는 이력을 두지 않습니다. 지난 모습을 다시 볼 일이 없는 데이터도 마찬가지입니다. 남길 까닭 없이 공간만 먹기 때문입니다.

백엔드에서 만나는 이력 셋

리비전 이력은 한 분야의 도구가 아닙니다. 백엔드 개발자가 매일 만지는 것 가운데 셋만 봐도 모양이 같습니다. 무엇이 리비전이고 이력으로 무엇을 하는지만 다릅니다.

어디서 리비전 하나 이력으로 하는 일
소스 코드 커밋 하나 잘못된 커밋을 되돌린다 · 갈래를 병합한다
배포 배포 설정을 바꿀 때마다 생기는 판 새 배포가 문제를 일으키면 앞 판으로 롤백한다
위키 문서 문서를 한 번 저장할 때마다 생기는 판 편집을 취소한다 · 두 판을 견준다

소스 코드 쪽의 대표는 Git 입니다. Git 에서는 커밋이 곧 리비전입니다. 커밋마다 부모 커밋을 가리킵니다.

배포 쪽의 예는 Kubernetes 입니다. Kubernetes 는 컨테이너를 여러 서버에 띄워 관리하는 도구입니다.

Kubernetes 안에서 애플리케이션 하나의 배포를 맡는 설정 단위가 디플로이먼트입니다. 디플로이먼트는 롤백할 옛 판을 몇 개까지 남길지를 설정으로 정합니다.

위키 문서 쪽의 예는 위키백과를 돌리는 MediaWiki입니다. 문서를 저장할 때마다 판이 하나씩 쌓입니다. 그 판 목록에서 두 판을 견주거나 편집을 취소합니다.

관련 항목

리비전 이력을 이루는 구성 요소

리비전 · 커밋 · 부모 커밋 · 메타데이터 · 커밋 메시지 · 스냅샷

리비전 이력이 갈라지고 합쳐지는 동작

브랜치 · 병합 · 병합 커밋 · 패스트 포워드 · 세 갈래 병합 · 공통 조상

리비전 이력을 고쳐 쓰는 동작

리베이스 · 스쿼시 · 체리픽 · 이력 강제 덮어쓰기

리비전 이력을 읽어 하는 작업

롤백 · 되돌리기 · diff · git bisect · git blame

리비전 이력이 놓이는 그래프 구조

DAG · 커밋 그래프 · 연결 리스트 · HEAD

리비전 이력의 크기를 줄이는 작업

컴팩션 · 보존 기간 · 가비지 컬렉션 · 델타 압축 · 툼스톤

리비전 이력을 두는 시스템

버전관리 · Git · Subversion · Kubernetes · 디플로이먼트 · etcd · MediaWiki

리비전 이력처럼 변경을 차례로 쌓는 기록

이벤트 소싱 · WAL · 감사 로그 · MVCC · 변경 데이터 캡처

다른 이름: revision history · 변경 이력 · 버전 이력