버전관리
파일이 어떻게 바뀌어 왔는지를 남겨 두는 일입니다. 남겨 둔 기록을 보면 언제 무엇이 바뀌었는지 알 수 있습니다. 예전 시점의 모습으로 되돌릴 수도 있습니다.
상세
가구 배치를 바꾸기 전에 방을 한 장 찍어 둡니다. 바꿀 때마다 한 장씩 찍어 두면 마음에 들지 않을 때 원하는 사진을 골라 그 모습으로 되돌릴 수 있습니다. 의자 하나만 되돌리는 것이 아니라 방 전체가 그 사진 속 모습으로 돌아갑니다.
버전관리는 바뀐 이력을 남겨 두고 어느 시점으로든 되돌릴 수 있게 하는 일입니다. 본체는 세 가지입니다. 어느 시점의 모습을 통째로 붙잡아 두는 것이 하나입니다. 그 시점마다 누가 언제 왜 바꿨는지를 함께 붙여 두는 것이 둘입니다. 갈라진 두 줄기를 다시 하나로 합치는 것이 셋입니다. 고치는 사람은 여럿일 수 있습니다. 각자 자기 사본을 앞에 두고 동시에 고칩니다.
붙잡아 두는 단위는 파일 하나가 아닙니다. 여러 파일이 그때 어떤 모습이었는지를 한 묶음으로 붙잡습니다. 그래서 되돌리기도 묶음 단위로 일어납니다. 파일 하나만 어제로 돌리는 것이 아니라 어제 그 시점의 모습 전체로 돌아갑니다.
이력은 덧붙기만 하는 성질을 갖습니다. 새로 고친 것이 앞의 것을 덮어 지우지 않습니다. 앞의 모습은 그대로 남고 그 뒤에 새 모습이 붙습니다. 붙잡아 둔 시점들은 앞뒤로 이어져 줄기를 이룹니다. 줄기는 갈라질 수 있고 갈라진 것은 다시 합쳐질 수 있습니다.
flowchart TD
A[처음 모습] --> B[두 번째 모습]
B --> C[갈래 하나]
B --> D[갈래 둘]
C --> E[합친 모습]
D --> E
배경
여럿이 같은 파일을 고치면 나중에 저장한 것이 앞서 저장한 것을 덮어 지웁니다. 앞 사람이 한 일이 흔적 없이 사라집니다. 무엇이 언제 왜 바뀌었는지도 아무도 모릅니다. 잘못 고친 것을 알아차려도 돌아갈 자리가 없습니다.
그래서 고침 하나하나를 따로 남겨 두는 도구가 필요해졌습니다. 남길 때 판을 구별할 표시를 붙이고 누가 언제 왜 바꿨는지를 함께 적어 두는 방식입니다.
Rochkind 는 회고 논문에서 SCCS(Source Code Control System)가 1975년에 처음 소개됐다고 적습니다. 같은 글은 SCCS 가 판을 추적하고 누가 언제 왜 바꿨는지를 기록하는 방식으로 컴퓨터 프로그램 소스 코드를 통제했다고 적습니다. 저자 본인은 그것이 무엇에 기반한 것이 아니었다고 적습니다. 앞선 시스템이 있었더라도 자신도 함께 일한 누구도 그것을 몰랐다는 것입니다. 저자는 자신이 다루는 범위를 버전관리 시스템으로 한정하면서 SCCS 가 그중 널리 알려진 첫 사례였다고 적습니다. 그 뒤 1985년 RCS(Revision Control System) 논문은 버전관리를 여러 판과 구성으로 이루어진 소프트웨어 시스템을 잘 정돈된 상태로 유지하는 일이라고 적습니다. 같은 논문은 SCCS 를 RCS 의 선행자라고 부릅니다.
갈래
이력을 어디에 두느냐가 축입니다. 한곳에만 두느냐 참여자마다 두느냐에 따라 끊긴 상태에서 무엇이 되는지, 어느 것이 정본인지, 합치는 일이 언제 일어나는지가 갈립니다.
중앙집중식
Subversion 공식 문서는 Subversion 이 처음 설계되어 나올 무렵 버전관리의 지배적인 방법론이 중앙집중식이었다고 적습니다. 판이 매겨진 데이터를 담은 원격 마스터 저장고 하나를 두는 방식입니다. 사용자 각자는 그 데이터의 이력을 얕게 복사한 사본을 두고 로컬에서 작업합니다. Git 공식 문서도 같은 방식을 적습니다. 판이 매겨진 파일을 모두 담은 서버 하나가 있고 여러 클라이언트가 그 중앙에서 파일을 체크아웃합니다. 같은 문서는 이것이 여러 해 동안 버전관리의 표준이었다고 적습니다.
sequenceDiagram
participant U as 사용자
participant C as 중앙 저장고
U->>C: 체크아웃
C-->>U: 판이 매겨진 파일
Note over U: 이력은 얕은 사본만
U->>C: 커밋
Note over C: 새 리비전이 생긴다
정본은 중앙 저장고 하나입니다. 사용자 쪽에 있는 것은 그 이력의 얕은 사본입니다. 새 판은 중앙이 커밋을 받아들이는 순간 생깁니다.
분산형
Git 공식 문서는 분산형에서 클라이언트가 최신 스냅숏만 체크아웃하지 않는다고 적습니다. 클라이언트는 저장소를 전체 이력까지 포함해 완전히 복제합니다. 그래서 서버가 죽어도 클라이언트 저장소 가운데 아무것이나 서버로 도로 복사해 복원할 수 있다고 적습니다. 모든 클론이 모든 데이터의 완전한 백업이라는 것입니다. 같은 문서는 이런 시스템 가운데 다수가 여러 원격 저장소를 함께 다루는 일을 꽤 잘 처리한다고 적습니다. 그래서 중앙집중식에서는 가능하지 않은 여러 종류의 작업 흐름을 세울 수 있다고 적습니다. 계층 모델 같은 것입니다.
Subversion 공식 문서는 같은 방식을 밖에서 이렇게 적습니다. 가장 먼저 눈에 띄는 것은 원격의 중앙 저장고가 없다는 사실입니다. 사용자마다 매우 깊은, 어떤 의미에서는 완전한 로컬 이력 데이터 저장고를 두고 그것을 상대로 작업합니다. 협업은 여전히 일어납니다. 다만 중앙 마스터 데이터 저장고를 거치지 않고 사용자들의 로컬 데이터 저장고 사이에서 변경 묶음을 직접 주고받는 방식으로 이뤄집니다.
sequenceDiagram
participant A as 사용자
participant AR as 내 저장소
participant BR as 상대 저장소
A->>AR: 커밋
Note over AR: 전체 이력의 완전한 복제본
AR->>BR: 변경 묶음을 직접 주고받음
정본이 어디 하나로 정해져 있지 않습니다. 커밋은 자기 저장소에서 끝납니다. 합치는 일은 변경 묶음을 주고받는 시점에 일어납니다.
예시
커밋 객체 한 개
Git 공식 문서는 커밋 객체가 무엇을 담는지를 실물로 보입니다. 스냅숏을 누가 저장했는지, 언제 저장했는지, 왜 저장했는지가 그 객체에 들어갑니다.
$ echo 'First commit' | git commit-tree d8329f
fdf4fc3344e67ab068f836878b6c4951e3b15f3d
$ git cat-file -p fdf4fc3
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author Scott Chacon <[email protected]> 1243040974 -0700
committer Scott Chacon <[email protected]> 1243040974 -0700
First commit
fdf4fc3344e67ab068f836878b6c4951e3b15f3d 가 이 커밋의 이름입니다. 객체의 형식은 단순합니다.
그 시점 프로젝트 스냅숏의 최상위 트리를 적습니다. 부모 커밋이 있으면 그것을 적습니다. 위 커밋에는
부모가 없습니다. 작성자와 커미터 정보를 적고 시각을 함께 적습니다. 빈 줄 하나를 두고 커밋
메시지를 적습니다. 같은 문서는 커밋 객체를 만들 때 트리 해시 하나와 바로 앞선 커밋 객체를
지정한다고 적습니다. 여기서 쓰이는 해시는 SHA-1(Secure Hash Algorithm 1) 값입니다.
저장소 전체에 붙는 리비전 번호
Subversion 공식 문서는 번호를 매기는 방식이 다른 쪽을 보입니다. 클라이언트는 파일과 디렉터리를 몇 개든 하나의 원자적 트랜잭션으로 커밋합니다. 저장소가 커밋을 받아들일 때마다 파일 시스템 트리의 새로운 상태가 생깁니다. 그것을 리비전이라 부릅니다. 각 리비전에는 앞 리비전에 붙은 번호보다 1 큰 고유한 자연수가 배정됩니다. 갓 만든 저장소의 최초 리비전은 0번입니다. 그 안에는 빈 루트 디렉터리 하나밖에 없습니다.
같은 문서는 이 번호가 개별 파일이 아니라 저장소 트리 전체에 적용된다고 적습니다. 리비전 번호 하나가 트리 전체를 고릅니다. 리비전 N 은 N 번째 커밋 뒤 저장소 파일 시스템의 상태입니다. 같은 문서는 일반적으로 어떤 파일의 리비전 N 과 M 이 반드시 다르지는 않다고 덧붙입니다. 다른 많은 버전관리 시스템이 파일별 리비전 번호를 쓰기 때문에 이 개념이 처음에는 낯설게 보일 수 있다고 적습니다.
위키 문서의 판 이력
버전관리는 개발 도구 밖에서도 나타납니다. MediaWiki 공식 문서는 문서의 리비전 이력을 View
history 탭을 눌러 본다고 적습니다. 거기서 리비전 두 개를 고르고 Compare selected revisions 를
누르면 차이를 볼 수 있습니다. 화면은 가장 새로운 변경을 먼저 보이고 그 뒤에 오래된 것을 보입니다.
특정 판을 보려면 그 날짜를 누릅니다. cur 은 옛 판을 현재 판과 견주고 prev 는 그 판을 바로 앞
판과 견줍니다. 사소한 편집은 m 으로 표시됩니다. 같은 문서는 문서를 예전 판으로 되돌릴 수 있다고
적습니다. 삭제된 리비전의 내용은 필요한 권한이 없는 사용자에게는 열리지 않습니다.
관련 항목
이력을 이루는 단위
커밋 · 리비전 · 스냅숏 · 태그 · 해시 · 커밋 메시지 · 원자적 트랜잭션 · 오브젝트
줄기를 가르고 합치는 기법
브랜치 · 병합 · 병합 커밋 · 세 갈래 병합 · 리베이스 · 체리픽 · 패스트 포워드
이것을 실제로 구현·채택한 제품
Git · Subversion · Mercurial · Perforce · SCCS · RCS · Git LFS(Large File Storage)
저장소를 내려받아 두는 방식
원격 저장소 · 저장소 복제 · 체크아웃 · 작업 트리
여럿이 함께 쓰는 방법
풀 리퀘스트 · 코드 리뷰 · 브랜치 전략 · 페치 · 푸시 · 풀 · GitHub
이력의 쓰임
되돌리기 · 차이 비교 · 이등분 탐색 · 비난 조회 · 릴리스 태깅 · 시맨틱 버저닝 · 서명된 태그
자주 터지는 문제
병합 충돌 · 이력 강제 덮어쓰기 · 큰 이진 파일 · 비밀값 커밋
다른 이름: version control · 버전 관리 · revision control · 리비전 관리 · source control · 소스 코드 관리