Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 374 – Python 프로젝트를 위한 분산 VCS 선택

Author:
Brett Cannon <brett at python.org>, Stephen J. Turnbull <stephen at xemacs.org>, Alexandre Vassalotti <alexandre at peadrop.com>, Barry Warsaw <barry at python.org>, Dirkjan Ochtman <dirkjan at ochtman.nl>
Status:
Final
Type:
Process
Created:
07-Nov-2008
Post-History:
07-Nov-2008, 22-Jan-2009

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

근거

Python은 수년 동안 중앙 집중식 버전 관리 시스템(VCS; 처음에는 CVS, 현재는 Subversion)을 매우 효과적으로 사용해 왔습니다. Python 공식 버전의 마스터 사본을 보유하면 누구나 언제든 공식 Python 소스 코드를 얻을 수 있는 단일 장소가 제공됩니다. 또한 언어의 이력을 저장할 수 있었으며, 주로 개발을 지원하기 위해서였지만 후세를 위해서이기도 했습니다. 물론 VCS에서 V가 의미하는 버전 관리 기능은 개발할 때 매우 유용합니다.

그러나 중앙 집중식 버전 관리 시스템에는 단점이 있습니다. 무엇보다도 Python에서 버전 관리의 이점을 원활하게 누리려면 “핵심 개발자”(즉, Python 마스터 사본에 커밋 권한이 있는 사람)여야 합니다. 핵심 개발자는 아니지만 Python의 리비전 트리로 작업하려는 사람, 예를 들어 Python용 패치를 작성하거나 사용자 지정 버전을 만드는 사람은 리비전을 직접 지원하는 도구를 사용할 수 없습니다. 이는 상당한 제약이 될 수 있습니다. 이러한 비핵심 개발자는 이전에 저장한 상태로 변경 사항을 되돌리거나, 브랜치를 만들거나, 전체 리비전 이력과 함께 자신의 변경 사항을 게시하는 등의 기본 작업을 쉽게 수행할 수 없기 때문입니다. 비핵심 개발자에게 마지막으로 안전한 트리 상태는 Python 개발자들이 우연히 설정한 상태이며, 이로 인해 안전한 개발이 불가능해집니다. 이러한 이등 시민적 지위는 어떤 복잡도의 패치로든 Python에 기여하고 싶어 하며 개발을 더 쉽게 하기 위해 진행 상황을 점진적으로 저장할 방법을 원하는 사람들에게 방해가 됩니다.

또한 작업을 커밋하려면 온라인 상태여야 한다는 문제도 있습니다. 중앙 집중식 VCS는 모든 리비전을 저장하는 중앙 사본을 유지하므로 리비전을 저장하려면 인터넷에 접속해야 합니다. 인터넷이 없으면 커밋도 없습니다. 여행 중인데 인터넷을 전혀 사용할 수 없다면 이는 불편할 수 있습니다. Python에 기여하고 싶지만 인터넷 연결이 좋지 않아 커밋에 시간이 많이 들고 비용도 많이 드는 경우도 있으며, 이때는 한 번에 작업하는 편이 더 나을 수 있습니다.

중앙 집중식 VCS의 또 다른 단점은 개발자가 리뷰 의견에 따라 패치를 수정하는 것이 일반적인 사용 사례라는 점입니다. 중앙 집중식 모델에서는 중간 작업을 담아 둘 곳이 없으므로 이 작업이 더 어렵습니다. 전부 체크인하거나, 아무것도 체크인하지 않거나 둘 중 하나입니다. 중앙 집중식 VCS에서는 기능 브랜치나 버그 수정 브랜치에서 작업하는 동안 트렁크에 커밋되는 변경 사항을 추적하기도 매우 어렵습니다. 이로 인해 이러한 브랜치가 오래되어 최신 상태에서 뒤처지거나, 트렁크에 병합할 때 쉽게 해결할 수 없을 만큼 많은 충돌이 발생할 위험이 커집니다.

마지막으로 Python 유지 관리 문제도 있습니다. 어느 시점에나 개발 중인 Python 메이저 버전이 하나 이상 존재합니다(이 글을 작성할 당시에는 두 개입니다). 개발 중인 각 Python 메이저 버전에는 최소한 마지막 마이너 버전의 유지 관리 버전과 개발 중인 마이너 버전이 있습니다(예를 들어 2.6이 막 출시된 경우 2.6과 2.7이 모두 작업 중임을 의미합니다). 릴리스가 완료되면 코드베이스 사이에 브랜치가 생성되며, 한 버전의 변경 사항은 다른 버전에 속하지 않지만(속할 수도 있지만) 않습니다. 현재 중앙 집중식 VCS에는 이러한 시간상의 브랜치를 자연스럽게 지원하는 기능이 없으므로 브랜치를 모방하는 도구를 사용해야 합니다. 네 개의 활성 브랜치 사이에서 리비전을 병합해야 하는 경우가 많기 때문에 병합을 추적하는 일도 개발자에게 마찬가지로 고통스럽습니다(예: 2.6 유지 관리, 3.0 유지 관리, 2.7 개발, 3.1 개발). 이 경우 Subversion과 같은 VCS는 난해한 서드파티 도구를 통해서만 이를 처리합니다.

분산 VCS(DVCS)는 이러한 모든 문제를 해결합니다. 리비전 트리의 마스터 사본을 유지할 수도 있지만, 누구나 자신의 용도로 해당 트리를 자유롭게 복사할 수 있습니다. 이를 통해 누구나 온라인 또는 오프라인 상태에서 자신의 사본에 변경 사항을 커밋할 수 있습니다. 또한 유지 관리와 Python을 위한 새로운 기능 개발을 위해 리비전 트리의 이력에서 브랜치를 만드는 개념과도 더욱 자연스럽게 연결됩니다. DVCS는 중앙 집중식 VCS가 제공하지 않거나 제공할 수 없는 매우 다양한 추가 기능도 제공합니다.

이 PEP는 위에서 설명한 이점을 얻기 위해 Python에서 Subversion을 현재 널리 사용되는 DVCS 중 하나로 변경할 가능성을 검토합니다. 이 PEP가 끝날 때 DVCS로 전환된다는 것을 이 PEP가 보장하지는 않습니다. 뚜렷한 승자를 찾지 못하고 svn이 계속 사용될 가능성도 상당히 큽니다. 이러한 일이 발생하면 DVCS의 상태가 발전함에 따라 이 PEP를 향후 다시 검토하고 개정합니다.

용어

공통 용어에 합의하는 일은 놀라울 정도로 어렵습니다. 주된 이유는 각 VCS가 미묘하게 다른 작업, 객체 및 개념을 설명할 때 이러한 용어를 사용하기 때문입니다. 가능한 경우 개념에 대한 일반적인 정의를 제공하려고 하지만, 자세한 내용은 각 시스템의 용어집을 참조하십시오. 다음은 각 VCS에 관한 표준 웹 기반 참고 자료에서 가져온 용어 관련 기본 참고 자료입니다. 각 DVCS의 용어집도 참조할 수 있습니다.

브랜치
개발의 한 흐름으로, 시간순으로 정렬된 리비전 모음입니다.
체크아웃/작업 사본/작업 트리
개발자가 편집할 수 있으며 브랜치에 연결된 코드 트리입니다.
인덱스
리비전을 빌드하는 “스테이징 영역”이며, git에만 존재합니다.
저장소
브랜치로 구성된 리비전 모음입니다.
클론
브랜치 또는 저장소의 완전한 사본입니다.
커밋
저장소에 리비전을 기록하는 작업입니다.
병합
한 브랜치 또는 저장소의 모든 변경 사항과 이력을 다른 브랜치 또는 저장소에 적용하는 작업입니다.
원래 브랜치 또는 저장소에서 체크아웃/클론을 업데이트하는 작업으로, 원래 브랜치 또는 저장소는 원격일 수도 있고 로컬일 수도 있습니다
푸시/게시
한 저장소에서 다른 저장소로 리비전과 해당 리비전이 의존하는 모든 리비전을 복사하는 작업입니다.
체리 픽
한 브랜치에서 하나 이상의 특정 리비전을 다른 브랜치로 병합합니다. 이때 다른 저장소일 수도 있고, 종속 리비전이 포함되지 않을 수도 있습니다.
리베이스
브랜치를 “분리”하여 새로운 브랜치 지점으로 이동합니다. 커밋이 시간상 발생한 위치가 아니라 브랜치의 시작 부분으로 이동합니다.

일반적인 작업 흐름

현재 Python 코어 개발자의 일반적인 작업 흐름은 다음과 같습니다.

  • 커밋하거나 푸시할 수 있을 만큼 안정될 때까지 체크아웃에서 코드를 편집합니다.
  • 마스터 저장소에 커밋합니다.

상당히 단순한 작업 흐름이지만 단점이 있습니다. 우선 저장소와 관련된 모든 작업은 네트워크로 인해 시간이 걸리므로, 커밋과 푸시는 가능한 한 원자적이지 않을 수 있습니다. 또한 체크아웃 디렉터리를 재귀적으로 복사하는 방법을 제외하면 새 체크아웃을 반드시 저렴하게 생성할 수 있는 방법이 없다는 단점도 있습니다.

DVCS를 사용하면 작업 흐름은 다음과 같이 바뀝니다.

  • 마스터 저장소의 로컬 클론에서 브랜치를 분기합니다.
  • 원자적인 단위로 커밋하면서 코드를 편집합니다.
  • 브랜치를 메인라인에 병합하고
  • 모든 커밋을 마스터 저장소로 푸시합니다.

가능한 단계는 더 많지만, 현재 가능한 것보다 작업 흐름이 마스터 저장소로부터 훨씬 더 독립적입니다. 디스크 속도만큼 빠르게 로컬에서 커밋할 수 있으므로, 코어 개발자는 원자적 커밋을 훨씬 더 자주 수행하여 코드에 여러 작업을 한꺼번에 수행하는 커밋을 최소화할 수 있습니다. 또한 브랜치를 사용하면 다른 개발자가 수행하는 다른 변경 사항으로부터 변경 사항을 격리할 수 있습니다(원하는 경우). 브랜치는 비용이 저렴하므로 하나의 특정 문제, 예를 들어 버그 하나나 새 기능 하나를 해결하는 더 작은 브랜치를 여러 개 쉽게 만들고 유지 관리할 수 있습니다. DVCS의 더욱 정교한 기능을 사용하면 공식 메인라인이 진행되는 동안 장기간 실행되는 개발 브랜치를 개발자가 더 쉽게 추적할 수 있습니다.

후보

이름 약칭 버전 2.x 트렁크 미러 3.x 트렁크 미러
Bazaar bzr 1.12 http://code.python.org/python/trunk http://code.python.org/python/3.0
Mercurial hg 1.2.0 http://code.python.org/hg/trunk/ http://code.python.org/hg/branches/py3k/
git 해당 없음 1.6.1 git://code.python.org/python/trunk git://code.python.org/python/branches/py3k

이 PEP에서는 darcs, arch 또는 monotone을 고려하지 않습니다. 이러한 DVCS의 주요 문제는 다른 DVCS가 제공하는 매우 매력적인 기능을 제공하지 않는 한 지원할 만큼 충분히 널리 사용되지 않는다는 점입니다. Arch와 darcs에는 가까운 시일 내에 해결될 가능성이 낮아 보이는 심각한 성능 문제도 있습니다.

상호 운용성

사용할 DVCS를 이미 결정했으며 로컬 미러를 직접 유지 관리할 의향이 있는 경우, 세 가지 DVCS 모두 git의 “fast-import” 변경 집합 형식을 통한 교환을 지원합니다. 물론 git은 이를 기본적으로 지원하며, Bazaar의 기본 지원은 활발히 개발 중이고 2월 중순 기준으로 좋은 초기 평가를 받고 있습니다. 2009. Mercurial은 hg convert 명령을 통한 가져오기를 독특한 방식으로 지원하며, 내보내기에는 third-party fast-import support를 사용할 수 있습니다. 또한 Tailor 도구는 후보 형식 중 어느 형식이든 공식 저장소를 기반으로 하고 로컬 미러는 어느 형식이든 사용할 수 있는 미러를 자동으로 유지 관리하도록 지원합니다.

사용 시나리오

Subversion을 대체할 DVCS를 결정하는 데 도움을 주는 가장 좋은 방법은 핵심 개발자와 비핵심 개발자가 처리해야 하는 몇 가지 실제 사용 시나리오를 수행하는 데 무엇이 필요한지 살펴보는 것입니다. 각 사용 시나리오에서는 시나리오가 무엇인지, 기본 단계가 무엇인지에 대한 글머리 기호 목록(VCS마다 약간씩 다를 수 있음), 그리고 다양한 VCS( Subversion 포함)에서 해당 사용 시나리오를 수행하는 방법을 설명합니다.

별도로 명시하지 않는 한 각 VCS에는 각 시나리오의 구현을 작성하는 일을 담당하는 단일 저자가 있었습니다.

이름 VCS
Brett svn
Barry bzr
Alexandre hg
Stephen git

초기 설정

일부 DVCS에는 미리 초기 설정을 수행하면 얻을 수 있는 이점이 있습니다. 이 절에서는 사용 시나리오를 실행하기 전에 도구를 더 잘 활용하기 위해 수행할 수 있는 작업을 다룹니다.

모든 DVCS는 프로젝트 식별 정보의 설정을 지원합니다. 중앙 집중식 시스템과 달리, DVCS는 이메일 주소를 사용하여 커밋을 식별합니다. (액세스 제어는 일반적으로 ssh나 콘솔 로그인과 같이 DVCS 외부의 메커니즘을 통해 수행됩니다.) 이 식별 정보에는 전체 이름을 연결할 수 있습니다.

모든 DVCS는 이 정보의 대략적인 값을 얻기 위해 시스템을 조회하지만, 그 값이 원하는 값이 아닐 수도 있습니다. 또한 사용자별 및 프로젝트별로 이 정보를 설정하는 기능도 지원합니다. 이러한 속성을 설정하는 편의 명령은 서로 다르지만, 모두 구성 파일을 직접 편집할 수 있습니다.

일부 VCS는 체크아웃/체크인 시 줄 끝(EOL) 변환을 지원합니다.

svn

필수 사항은 없지만, dev FAQ의 guidelines을 따르는 것이 좋습니다.

bzr

설정은 필요하지 않지만, 로컬 브랜치를 훨씬 빠르고 공간 효율적으로 만들려면 모든 Python 브랜치를 보관할 공유 저장소를 생성해야 합니다. 공유 저장소는 실제로 .bzr 디렉터리를 포함하는 상위 디렉터리일 뿐입니다. bzr이 리비전을 커밋할 때, 리비전을 보관할 .bzr 디렉터리를 찾기 위해 로컬 디렉터리에서 파일 시스템을 따라 상위 방향으로 검색합니다. 여러 브랜치에서 리비전을 공유하면 사용되는 디스크 공간을 줄일 수 있습니다. 다음과 같이 하십시오.:

cd ~/projects
bzr init-repo python
cd python

이제 모든 Python 브랜치는 ~/projects/python내부에 생성해야 합니다.

Python 코드와 상호 작용하기 위한 기본값을 설정하려면 ~/.bzr/bazaar.conf~/.bzr/locations.conf 파일에 설정할 수 있는 항목도 있습니다. 어느 것도 필수는 아니지만, 일부는 권장됩니다. 예를 들어 모든 커밋에 gpg 서명을 추가하는 것이 좋지만, 개발자에게는 장벽이 너무 높을 수도 있습니다. 또한 기본적으로 브랜치를 푸시할 위치에 따라 기본 푸시 위치를 설정할 수 있습니다. 마스터 브랜치에 대한 쓰기 권한이 있다면 해당 푸시 위치는 code.python.org일 수 있습니다. 그렇지 않다면 Launchpad와 같은 무료 Bazaar 코드 호스팅 서비스일 수 있습니다. Bazaar를 선택한다면 정책과 권장 사항을 결정해야 합니다.

최소한 이메일 주소를 설정하십시오.:

bzr whoami "Firstname Lastname <email.address@example.com>"

아래의 hg와 git에서처럼 이메일 주소(또는 실제로는 거의 모든 매개변수)를 저장소별로 설정하는 방법이 있습니다. 다른 DVCS와 마찬가지로 ini 스타일 형식을 사용하는 $HOME/.bazaar/locations.conf 파일의 설정을 통해 이를 수행합니다. 자세한 내용은 Bazaar 문서를 참조하십시오. 다만 대부분의 내용은 이 논의와 관련이 없습니다.

hg

최소한 사용자 이름은 설정해야 합니다. 그렇게 하려면 홈 디렉터리에 .hgrc파일을 생성하고 다음 내용을 추가하십시오.:

[ui]
username = Firstname Lastname <email.address@example.com>

Windows를 사용하고 도구가 유닉스 스타일 개행을 지원하지 않는 경우, 구성에 추가하여 자동 개행 변환을 활성화할 수 있습니다.:

[extensions]
win32text =

<repo>/.hg/hgrc를 사용자 지정하여 특정 저장소에 대해 이러한 옵션을 로컬로 설정할 수도 있으며, ~/.hgrc를 사용할 필요는 없습니다.

git

필요하지 않습니다. 그러나 약간의 준비만 하면 git에서 작업을 원활하게 할 수 있는 여러 기능을 지원합니다. git에서는 작업 공간, 사용자 및 시스템 수준에서 기본값을 설정할 수 있습니다. 시스템 수준은 이 PEP의 범위를 벗어납니다. 사용자 구성 파일은 유닉스 계열 시스템에서 $HOME/.gitconfig이고, 작업 공간 구성 파일은 $REPOSITORY/.git/config입니다.

git-config 도구를 사용하여 user.name 및 user.email에 대한 기본 설정을 시스템 로그인 계정 전체에 적용하거나 특정 git 작업 사본에 로컬로 설정할 수 있으며, 위의 Mercurial 절에 표시된 것과 동일한 형식의 구성 파일을 편집할 수도 있습니다.:

# my full name doesn't change
# note "--global" flag means per user
# (system-wide configuration is set with "--system")
git config --global user.name 'Firstname Lastname'
# but use my Pythonic email address
cd /path/to/python/repository
git config user.email email.address@python.example.com

Windows를 사용한다면 git-config를 사용하여 core.autocrlf 및 core.safecrlf 기본 설정을 true로 지정하는 것이 좋습니다.:

# check out files with CRLF line endings rather than Unix-style LF only
git config --global core.autocrlf true
# scream if a transformation would be ambiguous
# (eg, a working file contains both naked LF and CRLF)
# and check them back in with the reverse transformation
git config --global core.safecrlf true

저장소에는 일반적으로 VCS에 등록하지 않아도 되는 파일 이름을 지정하는 .gitignore 파일이 포함되지만, 개인적인 규칙(예: “.msg”라는 임시 파일에서 항상 로그 메시지를 편집하는 것)을 지정하고 싶을 수도 있습니다.:

# tell git where my personal ignores are
git config --global core.excludesfile ~/.gitignore
# I use .msg for my long commit logs, and Emacs makes backups in
# files ending with ~
# these are globs, not regular expressions
echo '*~' >> ~/.gitignore
echo '.msg' >> ~/.gitignore

다른 VCS와 마찬가지로 여러 브랜치를 사용하는 경우 모든 객체를 공통 객체 저장소에 배치하여 공간을 많이 절약할 수 있습니다. 브랜치의 원본이 서로 다른 저장소에 있었다면 이 방식으로 다운로드 시간도 절약할 수 있습니다. 상위 저장소에 객체가 없었더라도 저장소의 브랜치 간에 객체가 공유되기 때문입니다. git은 공간과 시간 면에서 매우 효율적이며 여러 최적화를 자동으로 적용하므로 이 구성은 선택 사항입니다. (예는 생략합니다.)

일회성 체크아웃

핵심 개발자가 메인라인에 포함할 수 있도록 검토할 수 있게, 핵심 개발자가 아닌 개발자로서 버그를 수정하는 일회성 패치를 작성하고 게시하고 싶습니다.

  • trunk를 체크아웃하거나 브랜치를 만들거나 복제하십시오.
  • 코드를 일부 편집하십시오.
  • 패치를 생성하십시오(VCS가 가장 잘 지원하는 방식, 예를 들어 브랜치 이력에 기반하십시오).
  • 검토자의 의견을 받고 문제를 해결하십시오.
  • 핵심 개발자가 커밋할 두 번째 패치를 생성하십시오.

svn

svn checkout http://svn.python.org/projects/python/trunk
cd trunk
# Edit some code.
echo "The cake is a lie!" > README
# Since svn lacks support for local commits, we fake it with patches.
svn diff >> commit-1.diff
svn diff >> patch-1.diff
# Upload the patch-1 to bugs.python.org.
# Receive reviewer comments.
# Edit some code.
echo "The cake is real!" > README
# Since svn lacks support for local commits, we fake it with patches.
svn diff >> commit-2.diff
svn diff >> patch-2.diff
# Upload patch-2 to bugs.python.org

bzr

bzr branch http://code.python.org/python/trunk
cd trunk
# Edit some code.
bzr commit -m 'Stuff I did'
bzr send -o bundle
# Upload bundle to bugs.python.org
# Receive reviewer comments
# Edit some code
bzr commit -m 'Respond to reviewer comments'
bzr send -o bundle
# Upload updated bundle to bugs.python.org

bundle 파일은 슈퍼 패치와 같습니다. 이 파일은 patch(1)로 읽을 수 있지만, 추가 메타데이터를 포함하므로 bzr merge에 전달하여 이력이 완전히 포함된 완전히 사용 가능한 브랜치를 생성할 수 있습니다. 아래의 Patch Review 절을 참조하십시오.

hg

hg clone http://code.python.org/hg/trunk
cd trunk
# Edit some code.
hg commit -m "Stuff I did"
hg outgoing -p > fixes.patch
# Upload patch to bugs.python.org
# Receive reviewer comments
# Edit some code
hg commit -m "Address reviewer comments."
hg outgoing -p > additional-fixes.patch
# Upload patch to bugs.python.org

hg outgoing에는 이를 위한 플래그가 없지만 대부분의 Mercurial 명령은 --git 명령을 통해 git의 확장 패치 형식을 지원합니다. 이를 사용자의 .hgrc 파일에서 설정하면 패치를 생성하는 모든 명령이 확장 형식을 사용합니다.

git

git diff master > stuff-i-did.patch로 패치를 생성할 수도 있지만, git format-patch | git am은 일반 patch가 처리할 수 없는 몇 가지 작업(빈 파일, 이름 변경 등)을 처리하는 요령을 알고 있습니다. git은 커밋 메시지에서 “Stuff I did”를 가져와 파일 이름 0001-Stuff-I-did.patch를 생성합니다. git-format-patch 형식에 대한 설명은 아래의 Patch Review를 참조하십시오.

# Get the mainline code.
git clone git://code.python.org/python/trunk
cd trunk
# Edit some code.
git commit -a -m 'Stuff I did.'
# Create patch for my changes (i.e, relative to master).
git format-patch master
git tag stuff-v1
# Upload 0001-Stuff-I-did.patch to bugs.python.org.
# Time passes ... receive reviewer comments.
# Edit more code.
git commit -a -m 'Address reviewer comments.'
# Make an add-on patch to apply on top of the original.
git format-patch stuff-v1
# Upload 0001-Address-reviewer-comments.patch to bugs.python.org.

변경 사항 되돌리기

핵심 개발자로서 메인라인에 포함할 준비가 되지 않은 변경 사항을 되돌리고 싶습니다.

  • 원하지 않는 변경 사항을 되돌리십시오.
  • 패치를 서버로 푸시하십시오.

svn

# Assume the change to revert is in revision 40
svn merge -c -40 .
# Resolve conflicts, if any.
svn commit -m "Reverted revision 40"

bzr

# Assume the change to revert is in revision 40
bzr merge -r 40..39
# Resolve conflicts, if any.
bzr commit -m "Reverted revision 40"

되돌리려는 변경 사항이 마지막으로 적용된 변경 사항이라면 bzr uncommit만 사용하면 된다는 점에 유의하십시오.

hg

# Assume the change to revert is in revision 9150dd9c6d30
hg backout --merge -r 9150dd9c6d30
# Resolve conflicts, if any.
hg commit -m "Reverted changeset 9150dd9c6d30"
hg push

로컬 저장소에 커밋했지만 아직 다른 저장소로 푸시하지 않은 변경 사항을 되돌리려면 “hg rollback” 및 “hg strip”을 사용할 수 있다는 점에 유의하십시오.

git

# Assume the change to revert is the grandfather of a revision tagged "newhotness".
git revert newhotness~2
# Resolve conflicts if any.  If there are no conflicts, the commit
# will be done automatically by "git revert", which prompts for a log.
git commit -m "Reverted changeset 9150dd9c6d30."
git push

패치 검토

핵심 개발자는 다른 사람들이 제출한 패치를 검토하여 승인된 변경 사항만 Python에 추가되도록 확인하고자 합니다.

핵심 개발자는 다른 사람들이 제출한 패치를 검토해야 합니다. 그러려면 패치를 적용하고 테스트한 다음 변경 사항을 버려야 합니다. 핵심 개발자는 이미 trunk의 체크아웃/브랜치/클론을 가지고 있다고 가정할 수 있습니다.

  • trunk에서 브랜치를 분기하십시오.
  • 패치 제출자가 생성한 주석이 전혀 없는 상태로 패치를 적용하십시오.
  • 패치를 서버로 푸시하십시오.
  • 이제 쓸모없어진 브랜치를 삭제하십시오.

svn

이 PEP에서 정의된 의미의 “브랜치”라는 것이 존재하지 않으므로 Subversion은 이러한 개발 방식에 정확히 잘 맞지 않습니다. 대신 개발자는 패치를 테스트하기 위한 다른 체크아웃을 만들거나 서버에 브랜치를 만들어야 합니다. 지금까지 핵심 개발자들은 개별 패치를 처리할 때 “서버에 브랜치 만들기” 방식을 사용하지 않았습니다. 이 시나리오에서는 개발자가 작업할 trunk의 로컬 체크아웃을 만든다고 가정합니다.:

cp -r trunk issue0000
cd issue0000
patch -p0 < __patch__
# Review patch.
svn commit -m "Some patch."
cd ..
rm -r issue0000

한 번에 하나의 체크아웃만 실행하고 svn diff와 함께 svn revert -R를 사용하여 독립적으로 수행했을 수 있는 변경 사항을 따로 저장하는 방법도 있습니다.

bzr

bzr branch trunk issueNNNN
# Download `patch` bundle from Roundup
bzr merge patch
# Review patch
bzr commit -m'Patch NNN by So N. So' --fixes python:NNNN
bzr push bzr+ssh://me@code.python.org/trunk
rm -rf ../issueNNNN

또는 이러한 변경 사항을 trunk에 커밋할 가능성이 높으므로 체크아웃만 수행할 수도 있습니다. 그러면 로컬 작업 트리가 제공되며, 브랜치(즉, 모든 리비전)는 계속 서버에 남아 있게 됩니다. 이는 svn 모델과 유사하며 패치를 더 신속하게 검토할 수 있게 해 줄 수도 있습니다. 이 경우에는 푸시할 필요가 없습니다.:

bzr checkout trunk issueNNNN
# Download `patch` bundle from Roundup
bzr merge patch
# Review patch
bzr commit -m'Patch NNNN by So N. So' --fixes python:NNNN
rm -rf ../issueNNNN

hg

hg clone trunk issue0000
cd issue0000
# If the patch was generated using hg export, the user name of the
# submitter is automatically recorded. Otherwise,
# use hg import --no-commit submitted.diff and commit with
# hg commit -u "Firstname Lastname <email.address@example.com>"
hg import submitted.diff
# Review patch.
hg push ssh://alexandre@code.python.org/hg/trunk/

git

git-format-patch가 생성한 패치라고 가정합니다. 하나 이상의 패치를 포함하는 Unix mbox 파일이며, 각 패치는 RFC 2822메시지 형식으로 작성되어 있습니다. git-am은 다음과 같이 각 메시지를 커밋으로 해석합니다. 패치 작성자는 From: 헤더에서, 날짜는 Date 헤더에서 가져옵니다. 커밋 로그는 제목 줄의 내용, 빈 줄, 패치 시작 지점까지의 메시지 본문을 연결하여 생성합니다.:

cd trunk
# Create a branch in case we don't like the patch.
# This checkout takes zero time, since the workspace is left in
# the same state as the master branch.
git checkout -b patch-review
# Download patch from bugs.python.org to submitted.patch.
git am < submitted.patch
# Review and approve patch.
# Merge into master and push.
git checkout master
git merge patch-review
git push

백포트

코어 개발자로서 2.6, 2.7, 3.0 및 3.1에 패치를 적용하여 세 버전 모두의 문제를 수정하고 싶습니다.

항상 최첨단 버전과 최신 릴리스 버전을 개발 중으로 유지하고 있으므로, 현재 Python에서는 네 개의 브랜치가 동시에 작업되고 있습니다. 따라서 변경 사항이 여러 브랜치로 쉽게 전파되는 것이 중요합니다.

svn

Python은 svnmerge를 사용하므로 변경 사항은 트렁크(2.7)에서 시작한 다음 2.6의 릴리스 버전에 병합됩니다. 변경 사항을 3.x 계열에 반영하려면 3.1에 병합하고 수정한 다음 3.0에 병합합니다(2.7 -> 2.6; 2.7 -> 3.1 -> 3.0).

이는 패치를 2.6에 추가한 다음 이후 버전으로 포워드하는 포트-포워드 전략과 대조됩니다(2.6 -> 2.7 -> 3.0 -> 3.1).

# Assume patch applied to 2.7 in revision 0000.
cd release26-maint
svnmerge merge -r 0000
# Resolve merge conflicts and make sure patch works.
svn commit -F svnmerge-commit-message.txt  # revision 0001.
cd ../py3k
svnmerge merge -r 0000
# Same as for 2.6, except Misc/NEWS changes are reverted.
svn revert Misc/NEWS
svn commit -F svnmerge-commit-message.txt  # revision 0002.
cd ../release30-maint
svnmerge merge -r 0002
svn commit -F svnmerge-commit-message.txt  # revision 0003.

bzr

Bazaar는 리비전을 수동으로 체리 픽할 수 있으므로 여기서는 매우 간단합니다. 아래 예제에서는 리비전 번호 대신 리비전 ID를 지정할 수도 있지만, 일반적으로는 필요하지 않습니다. Martin Pool은 “일반적으로 지원되는 가장 오래된 브랜치에서 먼저 수정한 다음 이후 릴리스로 포워드 병합하는 것을 권장합니다.”라고 제안합니다.:

# Assume patch applied to 2.7 in revision 0000
cd release26-maint
bzr merge ../trunk -c 0000
# Resolve conflicts and make sure patch works
bzr commit -m 'Back port patch NNNN'
bzr push bzr+ssh://me@code.python.org/trunk
cd ../py3k
bzr merge ../trunk -r 0000
# Same as for 2.6 except Misc/NEWS changes are reverted
bzr revert Misc/NEWS
bzr commit -m 'Forward port patch NNNN'
bzr push bzr+ssh://me@code.python.org/py3k

hg

다른 DVCS와 마찬가지로 Mercurial은 Python 코어 개발자가 패치를 백포트할 때 사용하는 현재 워크플로를 제대로 지원하지 않습니다. 현재 버그 수정은 먼저 개발 메인라인(즉, 트렁크)에 적용한 다음 유지 관리 브랜치로 백포트하고, 필요한 경우 py3k 브랜치로 포워드 포트합니다. 이 워크플로에는 개별 변경 사항을 체리 픽할 수 있는 기능이 필요합니다. Mercurial의 transplant 확장은 이 기능을 제공합니다. 다음은 이 워크플로를 사용하는 시나리오의 예입니다.:

cd release26-maint
# Assume patch applied to 2.7 in revision 0000
hg transplant -s ../trunk 0000
# Resolve conflicts, if any.
cd ../py3k
hg pull ../trunk
hg merge
hg revert Misc/NEWS
hg commit -m "Merged trunk"
hg push

위 예제에서 transplant는 현재 svnmerge 명령과 매우 유사하게 작동합니다. 리비전 없이 transplant를 호출하면 여러 변경 사항을 이식하는 데 유용한 대화형 루프가 시작됩니다. 또 다른 유용한 기능은 변경 집합을 프로그래밍 방식으로 수정하는 데 사용할 수 있는 –filter 옵션입니다(예를 들어 Misc/NEWS에 대한 변경 사항을 자동으로 제거하는 데 사용할 수 있습니다).

기존 워크플로 대신, 버그 수정을 지원되는 가장 오래된 릴리스에 커밋한 다음 이러한 수정 사항을 더 최신 브랜치로 병합하여 변경 집합을 이식하지 않을 수도 있습니다.

cd release25-maint
hg import fix_some_bug.diff
# Review patch and run test suite. Revert if failure.
hg push
cd ../release26-maint
hg pull ../release25-maint
hg merge
# Resolve conflicts, if any. Then, review patch and run test suite.
hg commit -m "Merged patches from release25-maint."
hg push
cd ../trunk
hg pull ../release26-maint
hg merge
# Resolve conflicts, if any, then review.
hg commit -m "Merged patches from release26-maint."
hg push

이 접근 방식은 이력을 비선형적으로 만들고 따라가기를 약간 더 어렵게 하지만, 지원되는 모든 릴리스에서 버그를 수정하도록 유도합니다. 또한 백포트할 변경 사항이 많을 때 병합할 특정 리비전 ID를 찾을 필요가 없으므로 더 잘 확장됩니다.

git

git에서는 관련된 모든 마스터 저장소 브랜치를 포함하는 작업 공간을 갖게 됩니다. git cherry-pick은 저장소 간에 작동하지 않으므로 브랜치가 동일한 저장소에 있어야 합니다.

# Assume patch applied to 2.7 in revision release27~3 (4th patch back from tip).
cd integration
git checkout release26
git cherry-pick release27~3
# If there are conflicts, resolve them, and commit those changes.
# git commit -a -m "Resolve conflicts."
# Run test suite. If fixes are necessary, record as a separate commit.
# git commit -a -m "Fix code causing test failures."
git checkout master
git cherry-pick release27~3
# Do any conflict resolution and test failure fixups.
# Revert Misc/NEWS changes.
git checkout HEAD^ -- Misc/NEWS
git commit -m 'Revert cherry-picked Misc/NEWS changes.' Misc/NEWS
# Push both ports.
git push release26 master

특정 브랜치에서 정기적으로 체리 피킹하는 대신 병합하고 있다면, 먼저 특정 커밋을 병합한 다음 되돌려서 해당 커밋이 나중에 실수로 병합되지 않도록 막을 수 있습니다. 이렇게 해도 체리 피킹으로 원하지 않는 패치를 가져오는 것은 막을 수 없으며, 이 기법을 사용하려면 병합하지 않으려는 모든 항목을 차단해야 합니다. 이 점에서 svn과 다른지는 확실하지 않습니다.

cd trunk
# Merge in the alpha tested code.
git merge experimental-branch
# We don't want the 3rd-to-last commit from the experimental-branch,
# and we don't want it to ever be merged.
# The notation "^N" means Nth parent of the current commit. Thus HEAD^2^1^1
# means the first parent of the first parent of the second parent of HEAD.
git revert HEAD^2^1^1
# Propagate the merge and the prohibition to the public repository.
git push

새 기능의 협업 개발

때때로 핵심 개발자는 여러 개발자와 함께 주요 기능을 작업하게 됩니다. 핵심 개발자로서 다른 개발자와 협업할 수 있도록 공용 위치에 기능 브랜치를 게시할 수 있기를 원합니다.

이를 위해서는 다른 개발자가 액세스할 수 있는 서버에 브랜치를 만들어야 합니다. 모든 DVCS는 저장소 호스트를 적절히 구성하면 개발자가 이미 커밋할 수 있는 호스트에 새 저장소를 만들 수 있도록 지원합니다. 이는 개념적으로 svn에 기존하는 샌드박스와 유사하지만, 저장소 초기화의 세부 사항은 다를 수 있습니다.

핵심 개발자가 아닌 개발자를 위해 어느 정도 공개적으로 액세스할 수 있는 다양한 저장소 호스팅 서비스가 있습니다. Bazaar에는 Launchpad가 있고, Mercurial에는 bitbucket.org가 있으며, git에는 GitHub가 있습니다. 또한 자체 서버를 관리하는 개발자를 위한 사용하기 쉬운 CGI 인터페이스도 모두 제공합니다.

  • trunk에서 브랜치를 만드십시오.
  • 서버의 브랜치에서 가져오십시오.
  • trunk에서 가져오십시오.
  • 병합을 trunk로 푸시하십시오.

svn

# Create branch.
svn copy svn+ssh://pythondev@svn.python.org/python/trunk svn+ssh://pythondev@svn.python.org/python/branches/NewHotness
svn checkout svn+ssh://pythondev@svn.python.org/python/branches/NewHotness
cd NewHotness
svnmerge init
svn commit -m "Initialize svnmerge."
# Pull in changes from other developers.
svn update
# Pull in trunk and merge to the branch.
svnmerge merge
svn commit -F svnmerge-commit-message.txt

작업이 완료되기 전에 사용할 DVCS를 결정했으므로 이 시나리오는 불완전합니다.

이슈 종속성의 분리

때때로 이슈를 작업하는 중에 현재 작업 중인 문제가 실제로 여러 작은 이슈로 이루어진 복합 이슈임이 분명해집니다. 현재 작업을 가져온 다음 별도의 이슈를 작업하기 시작할 수 있으면, 이슈를 하나의 크고 복합적인 작업 단위로 합치는 대신 개별 작업 단위로 분리하는 데 매우 유용합니다.

  • A 브랜치를 만드십시오(예: urllib에 버그가 있습니다).
  • 일부 코드를 편집하십시오.
  • A 브랜치가 의존하는 새 B 브랜치를 만드십시오(예: urllib 버그가 socket 버그를 드러냅니다).
  • B 브랜치에서 일부 코드를 편집하십시오.
  • B 브랜치를 커밋하십시오.
  • A 브랜치에서 일부 코드를 편집하십시오.
  • A 브랜치를 커밋하십시오.
  • 정리하십시오.

svn

svn의 저렴한 브랜치 생성 기능 부족을 보완하기 위해, 파일을 하나의 변경 목록과 연결하는 changelist 옵션이 있습니다. 이는 커밋 수준에서 연결할 수 있는 것만큼 강력하지는 않습니다. 또한 변경 목록 간의 의존성을 표현할 방법도 없습니다.

cp -r trunk issue0000
cd issue0000
# Edit some code.
echo "The cake is a lie!" > README
svn changelist A README
# Edit some other code.
echo "I own Python!" > LICENSE
svn changelist B LICENSE
svn ci -m "Tell it how it is." --changelist B
# Edit changelist A some more.
svn ci -m "Speak the truth." --changelist A
cd ..
rm -rf issue0000

bzr

다음은 소켓 버그를 수정하기 위해 우회하는 동안 일부 변경 사항을 임시로 감춰 두는 데 bzr shelf(현재 bzr의 표준 구성 요소)를 사용하는 접근 방식입니다.

bzr branch trunk bug-0000
cd bug-0000
# Edit some code. Dang, we need to fix the socket module.
bzr shelve --all
# Edit some code.
bzr commit -m "Socket module fixes"
# Detour over, now resume fixing urllib
bzr unshelve
# Edit some code

또 다른 접근 방식은 loom 플러그인을 사용합니다. loom은 스태킹 의존성을 자동으로 처리하므로 종속 브랜치 작업을 크게 단순화할 수 있습니다. loom을 종속 브랜치의 스택(loom 용어로는 “스레드”라고 합니다)이라고 생각해 보십시오. 스레드 스택을 위아래로 쉽게 이동하고, 스택 위쪽의 변경 사항을 하위 스레드에 병합하고, 스레드 간의 diff를 생성하는 등의 작업을 수행할 수 있습니다. 때로는 검토나 커밋을 위해 loom 스레드를 별도의 브랜치로 내보내야 하거나 내보내고 싶을 수도 있습니다. 상위 스레드는 하위 스레드의 모든 변경 사항을 자동으로 통합합니다.

bzr branch trunk bug-0000
cd bug-0000
bzr loomify --base trunk
bzr create-thread fix-urllib
# Edit some code. Dang, we need to fix the socket module first.
bzr commit -m "Checkpointing my work so far"
bzr down-thread
bzr create-thread fix-socket
# Edit some code
bzr commit -m "Socket module fixes"
bzr up-thread
# Manually resolve conflicts if necessary
bzr commit -m 'Merge in socket fixes'
# Edit me some more code
bzr commit -m "Now that socket is fixed, complete the urllib fixes"
bzr record done

보너스 점수를 위해, 다른 누군가가 방금 수행한 것과 정확히 같은 방식으로 socket 모듈을 수정했다고 가정해 보겠습니다. 어쩌면 이 사람이 fix-socket 스레드를 가져와 그것만 trunk에 적용했을 수도 있습니다. 이제 중복된 fix-socket 스레드를 삭제하고, 그 사람의 변경 사항을 loom에 병합할 수 있기를 원할 것입니다.

bzr down-thread trunk
# Get all new revisions to the trunk. If you've done things
# correctly, this will succeed without conflict.
bzr pull
bzr up-thread
# See? The fix-socket thread is now identical to the trunk
bzr commit -m 'Merge in trunk changes'
bzr diff -r thread: | wc -l # returns 0
bzr combine-thread
bzr up-thread
# Resolve any conflicts
bzr commit -m 'Merge trunk'
# Now our top-thread has an up-to-date trunk and just the urllib fix.

hg

한 가지 접근 방식은 shelve 확장을 사용하는 것입니다. 이 확장은 Mercurial에 포함되어 있지는 않지만 설치하기는 쉽습니다. shelve를 사용하면 변경 사항을 선택하여 임시로 따로 보관할 수 있습니다.

hg clone trunk issue0000
cd issue0000
# Edit some code (e.g. urllib).
hg shelve
# Select changes to put aside
# Edit some other code (e.g. socket).
hg commit
hg unshelve
# Complete initial fix.
hg commit
cd ../trunk
hg pull ../issue0000
hg merge
hg commit
rm -rf ../issue0000

Mercurial로 이 시나리오에 접근하는 다른 방법도 몇 가지 있습니다. Alexander Solovyov는 Mercurial의 메일링 리스트에서 몇 가지 alternative approaches를 소개했습니다.

git

cd trunk
# Edit some code in urllib.
# Discover a bug in socket, want to fix that first.
# So save away our current work.
git stash
# Edit some code, commit some changes.
git commit -a -m "Completed fix of socket."
# Restore the in-progress work on urllib.
git stash apply
# Edit me some more code, commit some more fixes.
git commit -a -m "Complete urllib fixes."
# And push both patches to the public repository.
git push

보너스 점수로, 시간을 들여 작업했는데 다른 누군가가 방금 수행한 것과 같은 방식으로 socket을 수정하고 이를 trunk에 반영했다고 가정해 보겠습니다. 이 경우 브랜치가 최신 상태가 아니므로 push가 실패합니다. 수정이 한 줄짜리였다면 문자 하나하나까지 정확히 같을 가능성이 매우 높습니다. git은 이를 알아차리므로 작업이 끝납니다. git이 조용히 병합합니다.

이번에는 운이 그다지 좋지 않다고 가정해 보겠습니다.:

# Update your branch.
git pull git://code.python.org/public/trunk master

# git has fetched all the necessary data, but reports that the
# merge failed.  We discover the nearly-duplicated patch.
# Neither our version of the master branch nor the workspace has
# been touched.  Revert our socket patch and pull again:
git revert HEAD^
git pull git://code.python.org/public/trunk master

Bazaar 및 Mercurial과 마찬가지로 git에도 패치 스택을 관리하는 확장이 있습니다. Andrew Morton의 원래 Quilt를 사용할 수도 있고, Mercurial Queues 또는 Bazaar loom과 유사한 방식으로 대규모 패치 집합의 패치 추적을 VCS에 통합하는 StGit(“stacked git”)도 있습니다.

Python 릴리스 수행

DVCS를 사용할 때 PEP 101은 어떻게 달라집니까?

bzr

변경되기는 하지만 크게 변경되지는 않습니다. 유지 관리 브랜치를 만들 때에는 svn cp를 수행하는 대신 새 위치로 푸시하기만 하면 됩니다. 태그는 완전히 다릅니다. svn에서는 태그가 디렉터리 복사본이지만, bzr(그리고 추측하건대 hg)에서는 특정 브랜치의 리비전을 가리키는 기호 이름일 뿐입니다. release.py 스크립트는 대신 bzr 명령을 사용하도록 변경해야 합니다. DVCS(특히 bzr)는 체리 피킹과 병합을 충분히 잘 수행하므로 유지 관리 브랜치를 더 일찍 만들 수 있을 가능성이 있습니다. bzr/hg 미러에서 릴리스를 수행해 보는 것은 유용한 연습이 될 것입니다.

hg

분명히 PEP 101 및 릴리스 스크립트에서 Subversion에 특정한 세부 사항을 업데이트해야 합니다. 특히 릴리스 태그 지정 및 유지 관리 브랜치 생성 프로세스를 Mercurial의 기능을 사용하도록 수정해야 합니다. 이렇게 하면 릴리스 프로세스의 특정 측면이 단순하고 효율적으로 정리됩니다. 예를 들어 Mercurial에서 태그는 특정 리비전을 가리키는 기호 이름일 뿐이므로 릴리스에 태그를 지정하거나 태그를 다시 지정하는 작업이 사소한 작업이 됩니다.

git

변경되기는 하지만 크게 변경되지는 않습니다. 유지 관리 브랜치를 만들 때에는 svn cp를 수행하는 대신 새 위치로 git push하기만 하면 됩니다. 태그는 완전히 다릅니다. svn에서는 태그가 디렉터리 복사본이지만, git에서는 브랜치와 마찬가지로 리비전을 가리키는 기호 이름일 뿐입니다. (태그와 브랜치의 차이는 태그가 특정 커밋을 가리키며, git tag -f를 사용하여 강제로 이동시키지 않는 한 절대 변경되지 않는다는 점입니다. 반면 체크아웃된 브랜치는 git commit에 의해 자동으로 업데이트됩니다.) release.py 스크립트는 대신 git 명령을 사용하도록 변경해야 합니다. git을 사용한다면 릴리스 엔지니어가 선정되는 즉시 (로컬) 유지 관리 브랜치를 만들 것입니다. 그런 다음 패치가 마음에 들지 않을 때까지 “git pull”을 수행하고, 그때는 “git pull; git revert ugly-patch”를 수행할 것입니다. 그러다가 분기하여 좋은 패치에 “git cherry-pick”을 수행하는 것이 현명해 보이기 시작할 때까지 계속할 것입니다.

플랫폼/도구 지원

운영 체제

DVCS Windows OS X UNIX
bzr 예 (설치 프로그램), tortoise 포함 예 (설치 프로그램, fink 또는 MacPorts) 예 (다양한 패키지 형식)
hg 예 (서드 파티 설치 프로그램), tortoise 포함 예 (서드 파티 설치 프로그램, fink 또는 MacPorts) 예 (다양한 패키지 형식)
git 예(제3자 설치 프로그램) 예(제3자 설치 프로그램, fink 또는 MacPorts) 예(.deb 또는 .rpm)

위 표에서 보듯이, 세 가지 주요 OS 플랫폼 모두에서 세 가지 DVCS를 사용할 수 있습니다. 하지만 이 표는 Bazaar가 바이너리 설치 프로그램으로 Windows를 직접 지원하는 유일한 DVCS인 반면, Mercurial과 git은 바이너리를 위해 제3자에 의존해야 한다는 점도 보여 줍니다. bzr과 hg에는 둘 다 tortoise 버전이 있지만 git에는 없습니다.

Bazaar와 Mercurial은 순수 Python으로 사용할 수 있고, 성능 향상을 위한 선택적 확장을 제공한다는 장점도 있습니다.

CRLF -> LF 지원

bzr
제가 이 글을 입력하는 동안 이 기능에 대한 지원 작업이 진행 중이며, 곧 출시될 버전에 포함될 것으로 알고 있습니다. 자세한 내용을 찾아보겠습니다.
hg
win32text 확장을 통해 지원됩니다.
git
개인적인 경험으로는 말씀드릴 수 없지만, core.autocrlf 및 core.safecrlf 구성 속성을 통해 상당히 잘 지원되는 것으로 보입니다.

대소문자를 구분하지 않는 파일 시스템 지원

bzr
문제없을 것입니다. 저는 Linux와 OS X 간에 브랜치를 항상 공유합니다. 대소문자 변경(예: bzr mv Mailman mailman)도 해 보았으며, 이를 Linux에서 수행하기만 하면(당연히 그렇습니다) OS X에서 변경 사항을 가져왔을 때 모든 것이 순조로웠습니다.
hg
Mercurial은 대소문자에 안전한 저장소 메커니즘을 사용하며 대소문자 접기 충돌을 감지합니다.
git
OS X는 대소문자를 보존하므로 그곳에서도 대소문자를 변경할 수 있습니다. git은 어느 방향으로 이름을 변경하든 문제가 없습니다. 하지만 대소문자를 구분하지 않는 파일 시스템 지원은 일반적으로 대소문자를 구분하는 파일 시스템에서 충돌이 발생할 때 경고하는 것을 의미합니다. git은 그렇게 하지 않습니다.

도구

Review BoardRietveld 같은 코드 리뷰 도구의 경우, 전자는 세 가지 모두를 지원하는 반면 후자는 hg와 git을 지원하지만 bzr은 지원하지 않습니다. Bazaar에는 아직 온라인 리뷰 보드가 없지만, 이메일 기반 리뷰와 트렁크 병합을 관리하는 여러 방법이 있습니다. Bundle Buggy, Patch Queue Manager (PQM), 그리고 Launchpad’s code reviews가 있습니다.

세 가지 모두 저장소를 온라인에 올리려는 사람들에게 기본적인 호스팅 지원을 제공하는 웹 사이트를 하나씩 운영하고 있습니다. Bazaar에는 Launchpad가 있고, Mercurial에는 bitbucket.org가 있으며, git에는 GitHub가 있습니다. Google Code에도 git을 이 서비스와 함께 사용하는 방법에 대한 지침이 있으며, 저장소를 보관하는 방법과 읽기 전용 미러로 동작하는 방법을 모두 설명합니다.

세 가지 모두 Buildbot에서도 지원되는 것으로 보입니다.

Subversion 위에서의 사용

DVCS svn 지원
bzr bzr-svn (서드파티)
hg 여러 서드파티
git git-svn

세 가지 DVCS 모두 svn을 지원하지만, git만 해당 지원 기능이 기본으로 제공됩니다.

서버 지원

DVCS 웹 페이지 인터페이스
bzr loggerhead
hg hgweb
git gitweb

세 가지 DVCS 모두 클라이언트와 서버 측에서 다양한 훅을 지원합니다. 예를 들어 커밋 전후 검증을 위한 훅이 있습니다.

개발

세 프로젝트 모두 활발히 개발되고 있습니다. Git은 월간 릴리스 일정을 따르는 것으로 보입니다. Bazaar는 시기 기반 월간 릴리스 일정을 따릅니다. Mercurial은 4개월 주기의 정시 릴리스 일정을 따릅니다.

특별 기능

bzr

Martin Pool은 다음과 같이 덧붙입니다: “bzr에는 안정적인 Python 스크립팅 인터페이스가 있으며, 공개 인터페이스와 비공개 인터페이스를 구분하고 변경 중인 API에 대한 폐기 유예 기간을 둡니다. 일부 플러그인은 다음 두 주소에 나열되어 있습니다: https://edge.launchpad.net/bazaarhttp://bazaar-vcs.org/Documentation”.

hg

Alexander Solovyov는 다음과 같이 논평합니다:

Mercurial에는 주요 이벤트를 위한 훅과 명령을 확장할 수 있는 기능을 갖춘 사용하기 쉬운 광범위한 API가 있습니다. 또한 Mercurial과 함께 배포되는 mq(mercurial queues) 확장 기능도 있으며, 이 기능은 패치 작업을 간소화합니다.

git

git에는 cvsserver 모드가 있습니다. 즉, CVS를 사용하여 git에서 트리를 체크아웃할 수 있습니다. 트리에 커밋할 수도 있지만 병합과 같은 기능은 없으며, 브랜치는 CVS 모듈로 처리되므로 베테랑 CVS 사용자라면 충격을 받을 가능성이 큽니다.

테스트/소감

어떤 DVCS를 사용할지, 또는 사용할 DVCS가 있는지에 대한 최종 결정을 내리는 업무가 공동 저자가 아니라 저(Brett Cannon)에게 맡겨졌으므로, 다양한 도구를 평가하면서 가능한 한 투명하게 진행하기 위해 제가 실행한 테스트와 소감을 기록하는 것이 공정하다고 느꼈습니다.

진입 장벽

파이썬 저장소를 체크아웃하는 데 걸리는 시간과 노력의 양은 매우 중요합니다. 어려움이나 시간이 지나치게 크다면 파이썬에 기여하려는 사람이 포기할 가능성이 매우 큽니다. 그렇게 되도록 내버려 둘 수는 없습니다.

핵심 개발자가 아닌 개발자인 것처럼 2.x 트렁크를 체크아웃하는 데 걸리는 시간을 측정했습니다. 시간은 zsh에서 time 명령을 사용하여 측정했으며, 공간은 du -c -h로 계산했습니다.

DVCS 샌프란시스코 밴쿠버 공간
svn 1:04 2:59 139 M
bzr 10:45 16:04 276 M
hg 2:30 5:24 171 M
git 2:54 5:28 134 M

이 수치를 svn과 비교할 때는 이것이 1:1 비교가 아니라는 점을 인식하는 것이 중요합니다. Svn은 모든 DVCS와 달리 전체 리비전 기록을 내려받지 않습니다. 따라서 svn은 네트워크를 통해 다운로드해야 할 정보가 더 적다는 사실만으로도 DVCS보다 초기 체크아웃을 훨씬 빠르게 수행할 수 있습니다.

기본 정보 기능의 성능

기록을 조회해야 하는 명령을 도구가 얼마나 잘 수행하는지 확인하기 위해 README 파일의 로그를 측정했습니다.

DVCS 시간
bzr 4.5초
hg 1.1초
git 1.5초

이 테스트에서 주목할 점은 git이 페이저를 사용하지 않고 로그를 가져오는 방법을 알아내는 데 다른 세 도구보다 더 오래 걸렸다는 것입니다. 일반적으로 페이저 사용은 좋은 기능이지만, 이를 자동으로 활성화하지 않도록 하는 데 시간이 걸렸습니다(실제로 기본 git 명령에는 페이저 사용을 비활성화하는 --no-pager 플래그가 있습니다).

기본 제공 도움말에서 사용할 명령 파악하기

저장소가 어떤 URL에서 복제되었는지 확인하는 명령이 무엇인지 알아보려고 했습니다. 이를 위해 도구 자체에서 제공하는 도움말이나 man 페이지만 사용했습니다.

Bzr이 가장 쉬웠습니다: bzr info. bzr help를 실행해도 원하는 내용은 표시되지 않았지만 bzr help commands를 언급했습니다. 해당 목록에는 의미가 통하는 설명과 함께 그 명령이 표시되어 있었습니다.

Git은 두 번째로 쉬웠습니다. git help는 많은 내용을 표시하지 않았고 모든 명령을 나열하는 방법도 제공하지 않았습니다. 그때 man 페이지를 확인했습니다. 여러 명령을 읽어 보다가 git remote를 발견했습니다. 명령 자체는 origin외에는 아무것도 출력하지 않았습니다. git remote origin을 시도하자 오류라고 표시하면서 명령 사용법을 출력했습니다. 그때 git remote show를 발견했습니다. git remote show origin을 실행하자 원하는 정보가 표시되었습니다.

hg에서는 원하는 정보를 스스로 찾아내지 못했습니다. 실제로 제가 원했던 것은 hg paths였지만, hg help에 표시된 “기호 경로 이름의 정의 표시”라는 설명만으로는 그것이 분명하지 않았습니다(PEP에서 이를 보고한 결과 Mercurial 개발자들이 hg paths명령의 사용법을 더 명확하게 알 수 있도록 문구를 명확히 했다는 점을 언급할 필요가 있습니다).

체크아웃 업데이트

오래된 저장소를 업데이트하는 데 걸리는 시간을 확인하기 위해, 700개 커밋만큼 뒤처진 저장소와 50개 커밋만큼 뒤처진 저장소(각각 3주 전과 1주 전 상태)를 업데이트하는 데 걸리는 시간을 측정했습니다.

DVCS 700개 커밋 50개 커밋
bzr 39초 7초
hg 17초 3초
git 해당 없음 4초

Note

Git은 특정 리비전에서 저장소를 체크아웃하는 것을 허용하지 않는 것처럼 보이므로 700 commits 시나리오에 해당하는 값이 없습니다.

Git은 git pull의 출력으로 특별히 언급할 가치가 있습니다. 각 파일의 변경 내역 정보를 나열할 뿐만 아니라, 해당 정보에 색상도 지정합니다.

결정

PyCon 2009에서 Mercurial을 사용하기로 결정했습니다.

Subversion보다 Mercurial을 선택한 이유

svn은 개발 팀을 훌륭히 지원해 왔지만, DVCS만큼 커밋 권한이 없는 사람들의 요구를 충족하지 못한다는 점은 인정해야 합니다. svn은 저장소에 대한 커밋 권한이 있는 사람에게만 버전 관리, 브랜칭 등의 기능을 제공하므로, 커밋 권한이 없는 사람들에게 방해가 될 수 있습니다. 하지만 DVCS에는 이러한 제한이 없으므로, 누구나 Python의 로컬 브랜치를 만들고 전체 svn 저장소를 복제할 때 따르는 부담 없이 직접 로컬 커밋을 수행할 수 있습니다. 누구나 핵심 개발자와 동일한 작업 흐름을 사용할 수 있도록 하는 것이 svn에서 hg로 전환한 핵심 이유였습니다.

누구나 자신의 브랜치에 쉽게 로컬 커밋을 할 수 있도록 하는 이점과는 별개로, 오프라인에서 빠르게 작업할 수 있다는 이점도 있습니다. hg는 모든 데이터를 로컬에 저장하므로 원격 서버에 요청을 보낼 필요 없이 로컬 디스크를 사용할 수 있습니다. 따라서 응답 시간이 크게 단축됩니다. 또한 인터넷에 연결할 수 없을 때 오프라인으로 사용할 수 있습니다. 하지만 이 이점은 사소하며, Subversion에서 전환하게 만든 주된 요인이라기보다는 단순한 부수적 이점으로 여겨집니다.

다른 DVCS보다 Mercurial을 선택한 이유

Git이 선택되지 않은 데는 세 가지 핵심 이유가 있습니다(Brett Cannon이 이 정확한 이유들을 나열한 PyCon 2009 라이트닝 토크를 참조하십시오. 토크는 3:45에 시작됩니다). 첫째, git의 Windows 지원은 검토 중인 세 DVCS 가운데 가장 약한데, 이는 Python이 자신이 실행되는 모든 플랫폼에서 개발을 지원해야 하는 만큼 받아들일 수 없는 수준입니다. Python이 Windows에서 실행되고 실제로 그 플랫폼에서 개발하는 사람들이 있는 만큼, 탄탄한 지원이 필요합니다. 그리고 git의 지원이 개선되고 있기는 하지만, 현재로서는 문제로 간주할 만큼 충분히 큰 격차로 가장 약합니다.

둘째, 첫 번째 문제만큼이나 중요한 것은 Python 핵심 개발자들이 세 가지 DVCS 선택지 중 git을 큰 격차로 가장 덜 선호했다는 점입니다. 다음 표를 보면 핵심 개발자들을 대상으로 한 설문 조사 결과와, git이 얼마나 큰 격차로 가장 선호도가 낮은 버전 관리 시스템인지를 확인할 수 있습니다.

DVCS ++ 동률 무응답
git 5 1 8 13
bzr 10 3 2 12
hg 15 1 1 10

마지막으로, 다른 모든 조건이 동일하다면(사실 앞선 두 가지 문제에서 보았듯이 그렇지 않지만), C와 셸로 작성된 도구보다 Python으로 작성된 도구를 사용하고 지원하는 것이 바람직합니다. 저희는 단지 Python으로 작성되었다는 이유만으로 도구를 선택하지 않을 만큼 실용적이지만, 이번 경우처럼 합리적인 상황에서는 Python을 사용하는 도구를 장려하는 것의 유용성 또한 인정합니다.

Bazaar 대신 Mercurial이 선택된 이유는 인기도때문이었습니다. 핵심 개발자 설문 조사에서 나타났듯이, hg가 bzr보다 선호되었습니다. 하지만 git이 후보에서 제외되었다는 발표 이후 PyCon에서 드러났듯이, 커뮤니티 역시 hg를 선호하는 것으로 보였습니다. 많은 사람들이 Brett에게 다가와 다양한 방식으로 hg가 선택되기를 바란다고 말했습니다. bzr이 선택되는 것을 원하지 않는다고 말한 사람은 없었지만, 그렇다고 원한다고 말한 사람도 없었습니다.

이러한 모든 정보를 바탕으로, Guido와 Brett은 Mercurial을 Python의 차기 버전 관리 시스템으로 결정했습니다.

전환 계획

PEP 385는 svn에서 hg로의 전환 과정을 개괄합니다.