The Dream of Super Surplus Power

May the force be with you.

Hg Init: a Mercurial tutorial – Subversion Re-education

Mercurial Repository

서브버전을 사용했다면 머큐리얼은 혼란스러울 수 있다. 여기서는 머큐리얼이 동작하는 방식에서 가장 큰 차이를 다룬다. 만약 서브버전을 사용한 적이 없으면 다음으로 넘어가자.


Subversion Re-education

우리 회사 프로그래머들이 서브버전에서 머큐리얼로 바꾸기로 결정했을 때 난 정말 혼란스러웠다.

우선, 바꾸지 않아야 하는 온갖 이유를 댔다. ”저장소는 중앙 서버에 두는 게 안전합니다.“ 뭐라고? 내가 틀렸다. 머큐리얼에서는 모든 개발자가 자신의 하드 드라이브에 저장소 전체에 대해 복사본을 갖는다. 그게 실제 더 안전하다. 게다가 머큐리얼을 사용하는 거의 모든 팀은 중앙 저장소 역시 사용하며, 그 저장소는 강제로 백업하거나 3 단계 보안 구역을 만들어 사일론[1], 스톰트루퍼[2], 사랑스런 래브라두들[3](또는 IT 부서에서 필요하다면 그 외 무엇이든지)에 각 구역을 맡길 수도 있다.

”분산형 버전 관리는 브랜치를 너무 쉽게 만들 수 있다는 게 문제입니다. 그리고 브랜치는 항상 문제를 일으키죠.” 라고 난 얘기했다. 이 역시 틀린 것으로 밝혀졌다. 난 계속해서 틀렸다. 서브버전은 병합(merge)하는데 충분한 정보를 저장하지 않으므로 브랜치를 만들면 문제가 생긴다. 머큐리얼에서 병합은 고통도 없고 쉬우므로 브랜치는 흔하고 아무런 해도 없다.

그런 후 난 얘기했다. “좋아요, 사용하죠. 하지만 잘 이해할 거라고는 생각하지 마세요.” 난 제이콥(Jacob)에게 서브버전에서 일반적으로 사용했던 모든 기능에 대한 목록과 머큐리얼에서 그에 해당하는 기능을 쪽지로 만들어 달라고 요청했다.

이제 그 쪽지를 여러분에게 보여줄 수 있지만 그러지 않으려 한다. 왜냐하면 그 쪽지 때문에 몇 달 동안 내 머릿속이 엉망이었기 때문이다.

서브버전을 사용해 봤다면 여러분의 머릿속은 다소 음, 뭐라고 하면 정중해 보일까? 여러분은 뇌가 손상됐다. 아니 이건 정중하지 못하다. 여러분은 재교육을 좀 받아야 한다. 난 머큐리얼이 서브버전보다 복잡하다고 생각하며 6 개월 동안 뇌가 손상돼 있었다. 하지만 이는 단지 머큐리얼이 정말 어떻게 동작하는지 이해하지 못했기 때문이었다. 일단 이해한 후에는 정말 간단했다.

그래서 여러분을 위해 이 튜토리얼을 썼으며 이제는 더 이상 뇌 손상이 없으므로 서브버전 관점에서 기능을 설명하지 않도록 매우 조심했다. 세상에는 뇌가 손상된 사람이 매우 많다. 그래서 서브버전을 사용하던 이들을 위해, 가능한 많은 손상을 되돌려 깨끗한 상태에서 머큐리얼을 배울 수 있도록 시작 부분에 이 장을 마련했다.

서브버전을 사용해 본적이 없으면 다음 장(Ground Up Mercurial)으로 가도 된다.

준비됐나? 좋다, 간단한 문제로 시작하자.

문제 1. 여러분은 한 번에 완벽하게 코드를 만들 수 있는가?

이 질문에 ‘예’라고 답한다면 거짓말쟁이에 사기꾼이다. 불합격이니 다시 풀어라.

새 코드에는 버그가 많으며 적절히 동작하는 데는 시간이 걸린다. 이 때문에 팀에 있는 다른 개발자는 나쁜 경험을 할 수 있다.

서브버전은 다음처럼 동작한다.

  • 새 코드를 체크 인하면 다른 모든 개발자가 그 코드를 얻는다.

모든 새 코드에는 버그가 있으므로 다음 중 하나를 선택할 수 있다.

  • 버그투성이 코드를 체크 인해 다른 모든 개발자를 미치게 한다.
  • 버그를 완전히 해결할 때까지 체크 인을 미룬다.

서브버전에는 항상 이런 끔찍한 딜레마가 있다. 막 만든 새 코드를 넣어 저장소가 버그로 넘치거나 또는 막 작성한 새 코드는 저장소에 없거나.

서브버전 사용자에게 이런 딜레마가 없는 것은 상상할 수 없을 만큼 너무나 익숙하다.

서브버전을 사용하는 팀에 일원은 종종 몇 일 또는 몇 주나 아무것도 체크 인하지 않곤 한다. 서브버전 팀에서 신참은 빌드를 망가뜨릴까 봐 또는 선임 개발자인 마이크(Mike)를 괴롭히거나 그 외 무엇이든 간에 두려움에 코드를 체크 인하는 걸 끔찍해 한다. 한번은 빌드를 망가뜨린 체크 인에 대해 마이크가 엄청 화를 내며 인턴의 자리로 폭풍처럼 달려가 그의 책상에 있는 모든 물건을 확 쓸어버리고는 소리쳤다. “넌 오늘이 끝이야!” (실제 그렇게 되진 않았지만 그 불쌍한 인턴은 실제로 바지를 적셨다.)

체크 인에 대한 이 모든 두려움 때문에 버전 관리의 이점을 전혀 누리지 못한 채 몇 주에 걸쳐 코드를 만들고 체크 인을 도와줄 선임을 찾게 된다. 사용할 수 없는 버전 관리를 왜 하나?

다음은 서브버전을 사용할 때 모습을 보여주는 간단한 그림이다.

Subversion Repository

머큐리얼에서는 모든 개발자가 자기 데스크톱에 자신만의 저장소를 가진다.

Mercurial Repository

자신의 개인적인 저장소에 코드를 커밋(commit)할 수 있으며, 원하면 언제든지 버전 관리의 모든 이점을 누릴 수 있다. 코드를 조금이라도 더 개선한 논리적인 시점이 되면 언제든지 커밋할 수 있다.

일단 코드가 완전해지면 새 코드를 다른 개발자가 쓸 수 있도록 여러분의 저장소에서 다른 개발자가 변경 내용을 가져가는(pull) 중앙 저장소로 넣는다(push). 그러면 마침내 다른 개발자가 여러분의 코드를 볼 수 있다.

머큐리얼에서는 새 코드를 커밋하는 행위와 그 내용으로 다른 개발자를 괴롭히는 행위를 분리한다.

이는 자신이 변경해 커밋(hg com)한 내용을 다른 이가 가져갈 수 없다는 것을 의미한다. 많은 변경 내용이 안정적이고 모든 것이 제대로 되었을 때 주 저장소로 변경 내용을 넣으면(hg push) 된다.

큰 개념적 차이 한 가지 더

어떻게 모든 거리에 이름을 붙이는지 아는가?

Japan city map음, 일본에서는 꼭 그렇진 않다. 거리 사이 블록에 번호를 붙이고 아주 중요한 거리에만 이름을 붙인다.

서브버전과 머큐리얼에는 이런 비슷한 차이가 있다.

서브버전에서는 리비전(revision)에 대해 생각하길 좋아한다. 리비전이란 시간적으로 특정 시점에서 전체 파일 시스템이 보이는 모습을 나타낸다.

머큐리얼에서는 체인지셋(changeset)에 대해 생각한다. 체인지셋은 한 리비전과 다음 리비전 사이 변경 내용에 대한 간략한 목록이다.

오십보 백보다. 무슨 차이일까?

차이는 다음과 같다. 여러분과 내가 어떤 코드에서 함께 작업하고 있다고 해 보자. 우리는 그 코드에 브랜치를 만들고 각자의 작업 영역에서 많은 변경 내용을 따로 만들어 꽤 달라졌다.

우리가 병합할 때 서브버전에서는 양쪽 리비전(나와 여러분이 변경한 코드)을 모두 살펴보고 어떻게 하나의 커다란 혼란 덩어리로 두드려 만들지 시도한다. 이는 보통 실패하며 병합 충돌(merge conflict) 기록을 만드는데, 실제 충돌이 아니라 서브버전에서 우리가 한 일을 이해하지 못해서이다.

대조적으로, 머큐리얼에서는 각자 작업하는 동안 일련의 체인지셋을 계속 유지하며 바쁘게 움직인다. 그러므로 병합하려 할 때 머큐리얼에는 실제 더 많은 정보가 있으므로 최종 산출물만 보고 어떻게 합칠지 시도하는 게 아니라 각자가 무엇을 변경했는지 알고 그러한 변경 내용을 다시 적용할 수 있다.

예들 들어, 어떤 함수를 조금 바꿔 어딘가로 옮긴다고 하자. 서브버전에서는 그런 단계를 기억하지 않으므로 병합할 때 새 함수가 느닷없이 나타났다고 생각할지도 모른다. 반면에 머큐리얼에서는 함수를 바꾸고 옮긴 일을 각각 기억해 변경한 내용을 더 성공적으로 병합할 것이다.

머큐리얼에서는 모든 것을 체인지셋으로 다루므로 여러분은 그 체인지셋으로 무언가를 해야 한다. 변경 내용을 중앙 저장소에 넣어 다른 모든 이를 귀찮게 하는 대신 그 내용을 시험하기 위해 팀에 있는 다른 동료에게 전달(push)할 수도 있다.

이 모든 게 다소 혼란스럽더라도 걱정할 필요 없다. 이 튜토리얼을 통해 완벽히 이해하게 될 거다. 지금 가장 중요한 것은 머큐리얼에서는 리비전이 아니라 체인지셋 관점에서 생각하므로 서브버전보다 코드 병합을 더욱 잘 할 수 있다는 점이다.

이는 병합이 더 이상 악몽이 아니므로 자유롭게 브랜치를 만들 수 있다는 것을 의미한다.

재미있는 걸 알려 줄까? 나와 얘기했던 거의 모든 서브버전 팀에서는 같은 얘기를 조금씩 바꿔 들려줬다. 이는 너무 흔해 ‘서브버전 이야기 1번’이라고 붙였다. 이 얘기는 다음과 같다. 어떤 시점이 되면 코드에 브랜치를 만드는데 흔히 고객에게 전달할 배포판은 개발자가 작업하는 것과 별도로 나뉜다. 모든 팀은 내게, 브랜치를 나눴을 때는 모든 게 괜찮았는데 병합할 때는 악몽이었다고 얘기했다. 5분짜리 과정이었어야 할 것이 2주 동안 프로그래머 여섯이서 한 컴퓨터에 둘러 앉아 수정한 모든 내용을 하나씩 안정 빌드에서 개발 빌드로 직접 적용하는 걸로 끝났다.

그리고 거의 모든 서브버전 팀에서는 “다시는 안 합니다.” 라며 브랜치를 만들지 않을 거라 맹세했다. 이제는 새 기능을 만들 때마다 커다란 #ifdef 구역에 넣는다. 그렇게 해서 trunk 하나에서만 작업할 수 있으나 버그를 모두 해결할 때까지 고객은 새 코드를 볼 수 없으며 이는 솔직히 좀 우습다.

안정 코드와 개발 코드를 분리해 유지하는 것이 소스 코드 관리에서 정확히 의도하는 것이다.

머큐리얼로 바꾸면 깨닫지 못 할 수도 있지만 브랜치를 다시 만들 수 있으며 두려워할 필요도 없다.

즉 팀 저장소를 여럿 만들고, 작은 팀 내 프로그래머들은 이 저장소에서 새 기능을 함께 만들며 완료했을 때 주 개발 저장소에 병합할 수 있으며 그 코드는 잘 동작한다!

또한 QA 팀에서 코드를 시험하는 저장소를 만들고, 코드가 잘 동작하면 QA 팀에서 중앙 저장소로 넣어(push) 중앙 저장소에는 항상 견고하며 시험을 거친 코드만 있으므로 잘 동작한다!

게다가 별도 저장소에서 시험을 계속할 수 있고 잘 동작하면 그 내용을 주 저장소에 병합하고 아니면 버릴 수 있으므로 잘 동작한다!

큰 개념적 차이 마지막 한 가지

서브버전과 머큐리얼 사이에서 마지막으로 중요한 개념적 차이는 대단하지 않으나 모르면 발목 잡힐 수 있다.

서브버전에서는 기본적으로 파일에 대해 리비전 관리를 하지만 머큐리얼은 모든 하위 디렉터리를 포함해 전체 디렉터리에 대해 리비전 관리를 한다.

서브버전에서는 어떤 하위 디렉터리에서 변경 내용을 커밋하면 해당 디렉터리와 그 아래 모든 디렉터리에 있는 변경 내용만을 커밋한다. 그러므로 다른 하위 디렉터리에서 변경한 내용을 잊을 수 있다. 반면에 머큐리얼에서는 모든 명령을 항상 전체 트리 구조에 적용한다. c:code 에 코드가 있고 hg commit 명령을 실행하면 c:code 또는 어느 하위 디렉터리에 있든지 같은 결과가 나타난다.

이는 큰 차이는 아니다. 하지만 회사 전체가 사용하는 매우 큰 단일 저장소를 사용하며, 일부 사람은 그 저장소에서 관심 있는 하위 디렉터리만 체크 아웃해 작업한다면 머큐리얼을 쓰는 게 그리 좋은 방법이 아니며 프로젝트별로 작은 저장소를 많이 만드는 게 더 낫다.

그리고 마지막으로….

다음에 하는 얘기는 있는 그대로 받아들여야 한다.

머큐리얼은 서브버전보다 더 낫다.

머큐리얼은 팀이든 혼자이든 소스 코드에 작업하기 더 좋다.

정말 더 낫다.

그리고 기억해 둘 것은 다음과 같다. 머큐리얼이 동작하는 방식을 이해하고 머큐리얼이 동작하는 방식으로 일하고 그에 맞서지 않으며, 머큐리얼을 사용해 서브버전 방식으로 하는 게 아니라 머큐리얼이 여러분에게 기대하는 방식으로 일하는 것을 배워라. 그러면 행복하고 성공적이며 잘 먹고 잘 살며 (맘 편하게) 항상 TV 리모컨을 찾을 수 있을 거다.

초기에는 머큐리얼을 포기하고 서브버전으로 돌아가고 싶은 유혹이 생길 거란 걸 안다. 낯설고 마치 외국에 사는 것처럼 향수병을 앓으며 모든 합리화를 생각해 낼 것이기 때문이다. 예를 들면 디렉터리에 대해 머큐리얼이 동작하는 방식은 너무 많은 공간을 차지한다며 주장하겠지만 사실 그렇지 않다. 보통은 서브버전 디렉터리보다 더 적은 공간을 차지한다. (사실이다!)

그러고는 서브버전에서 하듯 브랜치를 만들고 혼란스러워하며 잘 동작하지 않아 되돌아 갈 것이다. 서브버전에서 하는 대로가 아니라 사실 머큐리얼 방식대로 저장소를 복제(cloning)해 브랜치를 만들어야 한다. 머큐리얼 방식을 배우면 잘 동작한다, 날 믿어라.

그런 다음에는 제이콥이나 사무실에 있는 누구에게든 ‘머큐리얼에 대한 서브버전 대응 명령 쪽지’를 만들어 달라고 하고, hg fetch가 하는 일은 모른 채 svn up과 같다고 생각하며 세 달을 보낸다. 그러다 어느 날 모든 일이 잘못되고 머큐리얼이 어떻게 동작하는지 이해하지 않은 자신이 아니라 머큐리얼을 탓한다.

내가 그랬기에 여러분도 그렇게 할 거라는 걸 안다.

같은 실수를 하지 마라. 머큐리얼을 배우고 믿으며 동작 방식을 이해하면 소스 코드 제어에서 첨단으로 나아갈 것이다. 여러분의 경쟁자가, 공급자가 갱신한 라이브러리를 받아 모든 병합 충돌을 해결하느라 한 주를 바쁘게 보내는 동안 여러분은 hg merge를 입력하고 “이야 멋진 걸. 제대로 동작해.” 라고 말할 거다. 그러면 마이크는 침착해지고 인턴과 담배를 나눠 필 것이다. 곧 봄이 되고 근처 대학 꼬마들은 두꺼운 파카 점퍼를 얇은 A&F 티셔츠로 갈아 입을 것이며 삶은 좋아질 거다.


[1] 배틀스타 캘럭티카(Battlestar Galactica)에 등장하는 인공지능 기계 생명체
[2] 스타워즈에 등장하는 제국군 최정예 병사
[3] 래브라도 레트리버와 푸들을 교배한 개