버전 관리에서 우리에게 필요한 것 중 하나가 상태(state)에 대한 미묘한 해석이다. 가상 공간에서 작업하는 우리는 어떤 디지털 대상을 이해하기 위한 유용한 인지적 링크로서 물리적 메타포를 적용하는 데 익숙하다. 웹 페이지 링크 색을 변경하는 예로 돌아가보자. 버전 관리의 철학적 사고를 훈련하기 전의 우리는 컴퓨터 화면 안에 물리적인 개체로 존재하는 파일 하나를 갖고 있고 그 파일을 변경하는 거라고 생각했다. 여전히 CSS 파일은 거기 그대로 있는데 링크 색은 바뀐다.
사실 컴퓨터(또는 Git) 관점에선 최소한 세 개의 파일이 존재한다. 하나는 변경하기 전 파란색 링크를 포함하는 파일이고, 다른 하나는 그 링크 색을 지정하는 라인을 변경한 작업 사본이며, 마지막 하나는 기존 파일을 대체해 새로 저장되는 파일이다.
그러나 그 갱신 메커니즘이 style.css가 여전히 우리에겐 하나의 파일로 보인다는 사실을 바꾸진 못한다. 의미론적으로 비트 더미를 style.css라는 이름의 하나의 파일로 보이게 하는 것은 매우 가치가 있다. 우리의 데이터를 찾을 수 있는 곳을 알려주기 때문이다. 여러 버전 사이의 차이점과 관련해 너무 많은 시간을 쏟는 것은 짜증나는 일이다. 따라서 그 파일이 무언지 알려주는 이름에 의지해 어떻게 진화되는지 알 수 있는 어떤 다른 방법이 있다면 더 좋을 것이다.
더 정확히 말하면 세 개의 서로 다른 파일이라기보다는 세 가지 상태(state)를 갖는 동일한 파일에 대한 얘기다. 내용이 바뀌더라도 이름은 여전히 같으므로, 논리적으로 동일한 파일이 맞다.
파일과 디렉터리에 숫자를 매긴 예제와 Git과 같은 진자 버전 관리 시스템 사이에 혼동할 수 있는 차이점 하나는, 후자의 경우 과거 버전들로 가득 찬 거대한 폴더 따위는 존재하지 않는단 점이다. Git 사용자는 자신의 작업 트리의 각 논리 사본(logical copy) 내부에서 Git이 모든 과거 버전을 이전 상태와 함께 안전하게 저장할 것임을 기대한다.
두 개의 파일이나 디렉터리가 각자 서로의 사본이며 둘 중 하나는 나머지 하나에 비해 좀 더 최신이거나 발전된 것임을 알 수 있는, 그런 시스템을 우리는 쉽게 이해할 수 있다. 왜냐하면 원고도 두 번째 사본이 있고 책도 2쇄가 있는 현실 세계와 동일하기 때문이다. 앞서 설명한 초보적인 버전 관리 방법 역시 이해하기 쉽다. 단순한 컴퓨터의 파일 복사나 이동 등 평상시 하던 작업이기 때문이다. 그러나 Git과 같은 모델은 원고 집필보단 시간 여행에 가깝다.
앱이나 운영체제는 하드 디스크에 저장되어 있는 파일과 관련한 모든 복잡성을 숨기거나 추상화하는 환상적인 일을 수행한다. 하드 디스크와 메모리 사이를 오가는 광풍 대신, 우리는 아이콘 하나를 볼 뿐이다. 때때로 파일 내용이 변경됨에 따라 '마지막으로 저장된 시간'은 갱신되긴 하지만, 시각적으로나 의미상으로 늘 동일한 파일로 존재한다.
Git은 그런 종류의 복잡함을 숨기는 데 매우 서투를 뿐만 아니라 거의 시도하지도 않는다. 지나친 단순화는 오히려 Git에 해롭다. 우리가 매일 사용하는 많은 툴과 달리 Git은 어던 대상을 익숙한 메타포나 심볼로 맵핑하는 일을 거의 하지 않는다. Git은 사용자가 버전 관리 시스템의 원리뿐만 아니라 특별히 Git만의 작동 방식까지 알고 있다고 가정하고 설계됐다. 즉 사용자가 Git의 언어로 Git과 소통하도록 의도된 것이다.
Git은 분산형(decentralized) 버전 관리 시스템이다. 작업 사본은 물론 프로젝트의 전체 히스토리에 해당하는 완전한 사본이 로컬 컴퓨터, 서버, 그 프로젝트를 호스팅하는 모든 컴퓨터에 존재한다. 기본적으로 Git의 숨겨진 저장소 폴더는 작업 사본 폴더 안에 존재하지만 그 디렉터리에 들어가 보면 작업 사본에서 봐야 하는 파일과 폴더만 보인다. 그곳이 변경 작업을 할 장소다.
커밋(commit)은 객체(object)라고 하는 Git 데이터의 한 종류다. 파일 내용, 폴더의 구조 정보, 그리고 가장 중요한 프로젝트 버전을 표시하는 커밋 등 Git이 알고 있는 모든 정보는 내부적으로 객체에 저장되며, 각 객체는 담고 있는 정보에 따라 고유한 이름을 갖는다. 사실 객체의 이름(또는 식별자)은 단순히 이름이 아닌, 객체를 서로 구별하기 위해 컴퓨터가 판독할 수 있는 작은 지문이다.
의미론적으로 각 커밋은 주어진 어느 한 순간의 프로젝트 상태를 나타내는 완전한 스탭샷이다. 커밋의 고유 식별자는 다른 순간에 프로젝트 파일이 보인 모습으로부터 현재 순간의 상태를 구별할 수 있게 해 준다.