Skip to main content

Git vs. Mercurial

About 3 minGitArticle(s)blogd2.naver.comgitvirtual-tread

Git vs. Mercurial 관련

Git > Article(s)

Article(s)

Git vs. Mercurial | NAVER D2
Git vs. Mercurial

기존의 버전 관리 시스템(Version Control System)에서는 애자일(Agile) 스타일의 개발 방법론으로 개발을 진행할 때 잦은 충돌로 인해 브랜치를 병합하는 과정에서 예상보다 더 많은 비용이 드는 사례가 늘고 있습니다. 이런 환경에서 부각되고 있는 것이 분산 버전 관리 시스템(DVCS, Distributed VCS)입니다. 이 글에서는 분산 버전 관리 시스템의 대표적 제품인 Git과 Mercurial을 비교해 보겠습니다.


DVCS로의 이주

버전 관리 시스템(이하 VCS)은 IDE(Integrated Development Environment)와 개발자가 가장 자주 접하는 도구이다. VCS의 경우에는 IDE와는 달리 개발자에게 주어진 선택의 자유가 그렇게 많지 않다. 그 이유는 조직 내의 모든 구성원이 동일한 VCS를 사용해야 하기 때문이다. 하지만 아이러니하게도 개발자가 열심히 일할수록 종래의 VCS는 많은 문제를 일으킨다. 애자일(Agile) 개발 방법론을 적용하여 기능 단위의 변경이 동시에 병렬적으로 일어나는 환경에서는 자주 충돌이 발생한다. 브랜치를 병합하는 과정에서 역시 많은 충돌이 발생하고 병합에 들어가는 비용이 높아 종래의 의도대로 브랜치와 병합 기능을 사용하는 경우는 드물다.

이런 환경에서 분산 버전 관리 시스템이 점점 부각되기 시작했다. 특히 Bitkeeper나 Plastic SCM 등과 같은 상용 DVCS를 대체할 수 있는 Git나 Mercurial 등의 공개 도구가 등장하고, Linux 커널 등과 같은 대규모의 프로젝트에서도 사용되기 시작하면서 빠른 속도로 확산되기 시작했다. 특히 DVCS는 이력(History)에 대한 개념을 달리하면서 강력한 브랜치, 병합 등이 대표적인 장점이 되었다. 무엇보다 가장 큰 장점은 분산 환경에 기반하고 있으므로, 로컬 저장소(Repository)만으로 잘 동작하고, 짧은 역사를 만회하기 위해 기존 VCS와의 협업을 다양하게 지원한다는 점이다. 이러한 점은 개발자가 개발 환경에서 많은 비용을 들이지 않고 가벼운 마음으로 시도해 볼 수 있도록 한다.

DVCS를 사용해 보고 싶은 마음이 들었다면, 어떤 도구를 사용할 수 있는지 궁금할 수 있다. Darcs, Bazzar, arch, SVK, Fossil, Mercurial, Git 등의 공개 도구와 Bitkeeper, Plastic SCM 등의 상용 도구가 있는데, 이 중에서 현재 가장 대세에 가까운 Git와 Mercurial을 비교해 보도록 하겠다.


Git와 Mercurial

개요

Git과 Mercurial은 거의 비슷한 철학을 가지고 있다. 이는 아마도 DVCS가 가지는 특성 때문일 것이라고 생각할 수 있다. 가볍고, 규모 확장이 쉬운 VCS. 이 특성은 DVCS(Distributed VCS)에서 'D'가 가지고 있는 특징을 그대로 나타낸다. 특히 변경에 대한 이력을 시간에 의존적인 선형 구조로 나타내는 것이 아니라, 복수의 부모 변경과 복수의 자식 변경을 표시하는 그래프로 나타낸다. 두 제품에서 차이가 있다면, Git은 태생부터 수많은 병렬 브랜치를 전제로 하여 설계되었다는 점이다. Mercurial은 그런 장점이 없는 대신에 배우고 사용하기 쉽도록 많은 노력을 들였다. 이는 Git과 Mercurial 사이의 선택에서 가장 중요한 차이점이다.

실행 환경

Git은 C 언어와 bsh(Bourne Shell), Perl을 이용하여 전체가 작성되어 있으며, Mercurial은 거의 전체가 파이썬으로 작성되었다. 바꿔 말하자면 Git은 Mercurial에 비해 상당히 Linux 친화적인 성격을 가진다. 그렇기 때문에 Windows 환경에서 Git는 mingw32와 같은 에뮬레이션 환경이 필요하며, 상대적으로 Mercurial이 Git에 비해서 Windows 환경에서 더 나은 성능을 보여 준다. 하지만 동시에 Git은 셸 스크립트(shell script)를 사용할 수 있다면, C 언어를 사용하지 않아도 명령을 확장할 수 있다는 장점이 있다. 그에 반해서 Mercurial은 파이썬으로 된 코드(Core Code)나 명령어(Command) 구성을 이해하지 못하면 확장 명령을 만들기가 Git에 비해서는 어렵다는 특징이 있다.

사용성

사용성 측면에서 공구에 비유해 보자면 Mercurial은 종합 공구 세트에, Git은 맥가이버 칼에 비교할 수 있을 것 같다.

Mercurial은 이미 패키지 안에 모든 도구가 각각 고도로 완성된 상태로 들어 있는 종합 공구 세트이다. 물론 확장을 할 수는 있지만, 확장 명령을 만들기가 쉽지 않다. 대신 필요하다 싶은 기능은 대부분 이미 번들 확장(Extension)에 포함되어 있다.

반면에 Git은 필수적인 기능 세트를 콤팩트하고 세련된 형태로 갖춘 맥가이버 칼이다. 그래서 핵심적인 부분만을 봤을 때, 기능의 종류도 적고 기본적인 기능만 제공된다고 생각할 수 있다. 하지만 셸 스크립트를 사용해 기본적인 명령을 확장하거나 연결해서 새로운 명령을 만들어 자신의 손에 딱 맞는 형태의 도구를 생성할 수 있다. 그래서 때로는 Mercurial과 동등하거나 더 나은 사용성을 얻을 수 있다.

Mercurial은 또한 잘 정리된 별칭이 미리 만들어져 있으며, 대부분의 경우에 설정 없이 혹은 사용자 이름을 설정하는 정도만으로 바로 시작할 수 있는 환경을 제공한다. 그에 반해 Git은 일반적인 설정을 전혀 제공하지 않으므로, Mercurial에 비해 조금 더 많은 설정이 필요할 수 있다.

더 깊이

Git과 Mercurial의 차이점을 위에서 간략하게 살펴보았다. 이러한 차이는 구현 언어 외에도 구현 방식에서도 드러나는데, 대표적인 구현 방식상의 차이는 저장소의 구조와 브랜치에 있다.

저장소의 구성

먼저 Git의 저장소는 스냅샷 기반이다. 모든 변경이나 파일을 포함하는 대상은 오브젝트로 표현되며, 오브젝트의 종류에는 Commit, Tree, BLOB, Tag가 있다. BLOB은 Leaf 노드로 실제 관리되는 파일이 여기에 들어간다. 그 외의 오브젝트는 다른 오브젝트를 참조하는 형식으로 아래와 같은 트리 구조를 만든다.

Git의 오브젝트 트리 구조
Git의 오브젝트 트리 구조

여기서 BLOB에서의 관리 대상은 특정 시점의 파일 전체 내용이다. 그래서 Git의 저장소는 크기가 빠른 속도로 증가하는데, 이를 해결하기 위해 gc 명령을 제공한다. gc 명령을 수행하면서 접근 불가능한 브랜치는 삭제하고, 오래된 변경 집합(Changeset)은 diff 형태의 파일을 압축한 형태로 저장하여 저장소 효율을 높인다.

반면에 Mercurial은 각 파일별 변경분만 추적한다. 저장소에는 실제 관리 대상이 되는 파일의 트리 구조와 동일한 형태의 .i를 확장자로 하는 변경 기록용 파일이 있으며, 그 파일에는 해당 파일이 가리키고 있는 파일의 변경 이력이 바이너리(binary) 형태로 저장되어 있다. 그로 인해 저장소는 변경분에 비례해서 증가하게 되며, 일반적으로 Git에 비해 완만한 속도로 저장소의 크기가 증가한다.

Mercurial의 메타 정보 구조
Mercurial의 메타 정보 구조

그래서 별도의 저장소에 대한 관리 작업이 필요하지 않다는 장점이 있다. 대신 패치(patch)의 생성이나 변경 이력의 추적에는 Git에 비해서 용이하나, 스냅샷의 생성이나 업데이트, 커밋(Commit) 작업에는 대부분의 경우 Git에 비해서 높은 비용을 요구한다.

병합과 변경 이력

선형 구조와 Revision 구조의 차이
선형 구조와 Revision 구조의 차이

병합과 브랜치는 DVCS가 SVN등의 기존 VCS에 비해서 더 나은 가장 큰 장점 중 하나다. 분산 환경을 전제로 하고 개발된 시스템이므로 기본적으로 모든 변경 이력은 위 그림과 같이 DAG(Directed Acyclic Graph)로 나타나며, 그로 인해 SVN의 단순 스냅샷 기반의 3-Way 병합에 비해 영리한 병합 작업이 가능하다. 그것은 각 변경 집합이 부모 변경 집합에 대한 참조를 가지고 있기 때문인데, 이로 인해 충돌이 발생할 가능성이 현저하게 떨어지게 된다. 다만 구현상의 차이라기 보다는 동작상에 차이가 있다. Git은 정말 대규모의 브랜치를 고려하므로 n-Way 병합을 지원한다. 반면에 Mercurial의 경우 기본적으로 Git과는 다른 익명 브랜치(혹은 동적 브랜치)를 기본 사양으로 하므로, 2-Way 병합을 기본으로 하여, N개의 병합 대상 브랜치가 있다면 N-1의 병합을 실행하게 된다.

전반적으로 Mercurial이 제공하는 변경 이력 그래프는 Git에 비해 좁은 폭을 지니고 SVN의 이력 관리와 상당히 유사한 면모를 보인다. 그로 인한 대표적인 차이점이 Mercurial은 로컬 저장소에 있는 변경 이력에 대해 SVN의 리비전(Revision) 번호와 같은 리비전 번호를 제공한다. 다만 이 변경 이력이 복제된 다른 저장소에서도 유지된다는 것을 보장하지 못하므로, 식별자로서 사용하는 것은 의미가 없다. 그래서 이 차이점은 초기 적응에는 도움이 될 수 있지만, 최종적으로는 Mercurial에 대한 오해를 불러 일으키는 원인이 되기도 한다.

DVCS의 브랜치는 기본적으로는 저장소의 복제를 기반으로 동작한다. 하지만 Git과 Mercurial은 둘 다 태깅(Tagging)을 기본으로 하는 브랜치 기법을 제공한다. 분기된 특정 커밋에 태그를 붙이면, 해당 태그가 브랜치의 이름처럼 동작한다. 거의 동일하지만 큰 차이 중 하나는 저장소를 원격 저장소에 반영하거나 혹은 그 반대의 작업을 할 때, 브랜치에 대한 동작이다. Mercurial의 경우에는 모든 브랜치를 한 번에 반영한다. 그에 반해 Git은 현재 작업 중인 브랜치만 반영한다.

성능

Git 같은 경우 프로젝트의 규모에 비례하여 저장소의 규모가 상당히 빠른 속도로 증가하며, 대조적으로 Mercurial은 완만하게 선형적으로 증가하게 된다. 대신 스냅샷 기반이므로 대규모의 프로젝트에서도 일정한 성능을 유지하며, 대체로 Mercurial보다 빠르다. 반면 Mercurial의 경우 차이점 기반이며 디스크 I/O가 적으므로, 대량의 읽기/쓰기가 발생하는 상황에서도 비교적 안정적이다. 그러나 프로젝트 규모가 커지고, 변경 이력이 쌓일 수록 패치를 병합하는 비용이 증가한다. 또 Mercurial의 저장소는 Append 기반으로 동작함에 따라, 디스크에 오류가 있을 때에 저장소가 영향을 적게 받는다는 점도 Mercurial의 장점이 된다.


Subversion과의 연계

가장 첫머리에 밝혔다시피 일반적으로 개발자가 VCS를 마음대로 선택할 수 있는 경우는 드물다. 그러므로, 기존의 VCS와 함께 사용해야 할 텐데 가장 많이 쓰이는 VCS가 SVN(Subversion)이므로 SVN과의 연동을 기본으로 비교해보고자 한다.

둘 다 기본적인 동작은 SVN의 각 커밋 내용을 받아서 로컬 저장소에 다시 커밋하는 형태를 취한다. 이 과정에서 Git의 경우 중간에 동작을 멈추는 경우가 종종 있었으나 최근에는 해결되었는지 어지간한 규모의 프로젝트에서도 잘 동작한다. 그리고 기본적인 성능의 차이로 인해 Git이 Mercurial에 비해 조금 더 빠르다. Mercurial의 경우는 자체 메타 데이터 내에 SVN의 메타 데이터도 함께 보관한다. 그래서 일반적인 저장소에서의 상황과는 달리 SVN과 사용할 경우에는 Git의 저장소가 훨씬 콤팩트한 형태가 된다. 대신 SVN과의 연동 자체를 Mercurial이 Git보다 먼저 시작하였고 오래된 만큼 안정적이다.

이러한 문제가 생기는 원인은 SVN은 선형적으로 변경 사항의 목록을 관리하기 때문이다. DVCS의 프론트 엔드로 SVN을 쓸 때 가장 자주 부딪치는 문제가 이 그래프 형태의 변경 이력을 선형적인 형태로 바꾸는 작업이다. 이 문제를 해결한 방법이 Mercurial과 Git 양쪽 공히 rebase란 작업이다. 이 rebase 작업을 Mercurial의 경우 직접 해야 하고, Git의 경우는 원격 저장소에 반영하는 작업 속에 이 rebase가 녹아 있다.

일반적인 환경의 구성은 아래와 같다.

중앙집중형 저장소 구성
중앙집중형 저장소 구성

물론 DVCS답게 중앙 저장소를 가지지 않는 형태의 구성도 가능하지만, SVN과 함께 쓴다면 이런 형태 외에는 나오기가 힘들다. 다만 SVN과 DVCS를 같이 쓰거나 SVN에서 DVCS로 마이그레이션할 때, DVCS에서는 svn:externals와 같은 기능은 제공하지 않는다는 것, 하위 디렉터리별 체크아웃이 불가능하다는 점은 염두에 두어야 한다. 후자의 경우는 저장소의 재구성을 통해 해결하여야 하며, 전자의 경우는 유사한 확장(Mercurial - SubRepos, Git - SubModule)이 나와 있으나 기능적인 면에서 많은 차이를 보이니 사용 전에 미리 고려해 보는 것이 좋다.


결론

둘 다 DVCS의 대표적인 것들이며, 활발하게 수정이 일어나고 있다. 적용되고 있는 프로젝트도 늘어나고 있으며, Kernel이나 모질라(Mozilla), 안드로이드, OpenJDK 등 대규모 프로젝트의 이전도 눈에 많이 띈다. 뿐만 아니라 개발자 개인 환경에만 영향을 주는 설정이 가능하며, 둘 다 SVN과 나쁘지 않은 연동을 제공한다. 그렇게 봤을 때, 더구나 대부분의 소프트웨어가 그렇듯 서로의 장점을 흡수하며, 기능적으로는 빠른 속도로 닮아가고 있다. 그래서 선택에서 있어서는 기호에 맞추어 마음에 드는 것을 고르면 된다고 생각한다. 다만 간단하게 나마 고민을 줄일 수 있도록 몇몇 비교 가능한 요소를 위에서 정리한 내용의 일부를 포함하여 다음 표와 같이 간단하게 정리해 보았다.

참고

  • 상황에 따라 달라질 여지가 없는 경우
  • 추가 설정이 필요하거나, 경우에 따라서 달라질 수 있는 경우 혹은 일부만 지원되는 경우
  • 현재는 적용할 수 있는 방법이 없는 경우
비교 조건GitMercurial설명
SVN을 쓴 경험이 많고, 러닝커브를 최대한 줄여야 하는지
실행 환경이 윈도우 계열인지, xNIX 계열인지NIX윈도우윈도우 환경에서는 Mercurial이 압도적으로 빠르므로, Mercurial을 추천함
새로운 것을 경험해 보거나 DVCS에 대한 콘셉트를 잘 알고 싶은지
VCS가 아닌 다양한 용도로 사용할 필요가 있거나, 강력한 사용자화 혹은 명령 조합이 필요한지
호스팅 서비스https://github.comopen in new windowhttps://bitbucket.orgopen in new window
하위 디렉터리 체크아웃
외부 저장소 링크
권한 보존실행 권한만실행 권한만
변경 이력 모델스냅샷패치(Patch)
구현 언어C, Bourne Shell, Perl파이썬Mercurial은 바이너리비교기능(binary diff)만 C언어로 구현
브랜치 병합n-Way2-Way

부록

Mercurial 번들 확장 목록

다음 표는 Mercurial의 번들 확장 목록 중에서 자주 쓰는 일부 번들 확장이다. 더 자세한 목록이 필요하다면 공식 위키open in new window를 참조할 수 있다.

NamePageDescription
aclAclExtension저장소의 접근 경로별 커밋 권한을 설정한다. Mercurial 특성상 읽기 권한은 구분할 수 없다
aliasAliasExtension사용자가 정의한 커맨드 이름의 별칭을 사용할 수 있다.
bookmarksBookmarksExtension특정 변경 집합의 수정 사항을 따라가는 마커를 생성한다(Git의 Fast Branch와 유사한 기능을 제공)
bugzillaBugzillaExtension버그 ID 기록으로 버그질라의 항목을 업데이트할 수 있다.
childrenChildrenExtension특정 리비전이 부모인 자식 리비전을 보여 준다.
churnChurnExtension사용자별 작업의 통계를 보여 준다.
convertConvertExtension다른 VCS를 Mercurial로 변경한다. CVS, SVN, Git, Bazaar, Perforce 등을 지원한다.
colorColorExtensiondiff, status 등 일부 명령의 출력을 색깔로 표시한다.
eolEolExtensionEnd-line 문자를 작업 파일과 저장소간에 변환을 해준다.
extdiffExtdiffExtension외부 프로그램을 이용해서 diff 결과를 출력한다.
fetchFetchExtensionpull, merge, update를 한방에!
gpgGpgExtensionGPG를 이용해 change set을 digest하고 결과를 확인한다.
graphlogGraphlogExtension리비전 그래프를 ASCII로 출력한다.
hgciaHgciaExtensionCIAopen in new window로 알림을 보낸다
highlightHighlightExtensionMercurial의 웹 서버 상에서 파일 내용을 표시할 때 Syntax Highlight를 제공한다.
mqMqExtension패치를 큐(queue) 형태로 관리할 수 있도록 해준다.
progressProgressExtension일부 명령을 수행할 때 진행 상태를 표시한다(1.5 이상 필요)
rebaseRebaseExtension변경 집합의 부모를 변경한다
win32mbcsWin32mbcsExtension윈도우 상에서 파일명을 shift_jis/big5로 쓸 수 있도록

참고 자료

Git
Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
Mercurial: the definitive guide (Second Edition)

Git vs. Mercurial: Please Relax | Important Shock
Everyone's up in arms to embrace distributed version control as the new must-have tool for the developer in the know. Though many people have not yet migrated from Subversion, those that have almost invariably extoll the virtues of their particular choice. But though all of the major DVCS's have features that set them above the…
RockStarProgrammer - The Differences Between Mercurial and Git


이찬희 (MarkiiimarK)
Never Stop Learning.