명명 된 지점과 여러 리포지토리


130

우리는 현재 비교적 큰 코드베이스에서 서브 버전을 사용하고 있습니다. 각 릴리스에는 자체 분기가 있으며 트렁크에 대해 수정 사항이 수행되고 다음을 사용하여 릴리스 분기로 마이그레이션됩니다.svnmerge.py

나는 더 나은 소스 컨트롤로 옮겨 갈 시간이 왔다고 생각하며 머큐리얼과 잠시 동안 놀아 왔습니다.

Mercurial을 사용하여 이러한 릴리스 구조를 관리하는 학교는 두 곳인 것 같습니다. 각 릴리스는 자체 저장소를 가지며 릴리스 분기에 대해 수정 사항이 작성되고 기본 분기 (및 기타 최신 릴리스 분기)로 푸시되거나 단일 저장소 (또는 여러 개의 일치 사본) 내에서 이름이 지정된 분기를 사용합니다.

두 경우 모두 릴리스 브랜치에 포함시키기 위해 이식과 같은 것을 체리 픽 변경에 사용하는 것 같습니다.

나는 당신에게 묻습니다. 각 접근법의 상대적인 장점은 무엇입니까?

답변:


129

가장 큰 차이점은 분기 이름이 기록에 기록되는 방식입니다. 명명 된 분기를 사용하면 분기 이름이 각 변경 집합에 포함 되므로 이력의 불변 부분이됩니다. 복제본을 사용하면 특정 변경 세트의 출처에 대한 영구적 인 기록 이 없습니다 .

즉, 클론은 분기 이름을 기록하지 않으려는 빠른 실험에 적합하며 명명 된 분기는 장기 분기 ( "1.x", "2.x"및 유사)에 적합합니다.

또한 단일 저장소는 Mercurial에서 여러 개의 가벼운 분기를 쉽게 수용 할 수 있습니다. 이러한 저장소 내 브랜치는 책갈피를 지정하여 쉽게 다시 찾을 수 있습니다. 다음과 같이 회사 저장소를 복제했다고 가정 해 보겠습니다.

[a] --- [b]

당신은 멀리 해킹 및 확인 [x][y]:

[a] --- [b] --- [x] --- [y]

평균 누군가 풋 동안 [c][d]저장소에, 그래서 당신이 당길 때이 같은 역사 그래프를 얻을 :

            [x] --- [y]
           /
[a] --- [b] --- [c] --- [d]

단일 저장소에는 두 개의 헤드가 있습니다. 작업 사본에는 항상 소위 작업 사본 상위 변경 세트 인 단일 변경 세트가 반영됩니다. 이것을 확인하십시오 :

% hg parents

그것이보고한다고 가정 해 봅시다 [y]. 당신은 머리를 볼 수 있습니다

% hg heads

이것은보고 [y][d]. 리포지토리를의 깔끔한 체크 아웃으로 업데이트하려면 [d]간단히 ( [d]의 개정 번호로 대체 [d])하십시오.

% hg update --clean [d]

그러면 해당 hg parents보고서가 나타납니다 [d]. 이것은 당신의 다음 커밋이 [d]부모 가 될 것임을 의미합니다 . 따라서 메인 브랜치에서 발견 한 버그를 수정하고 변경 세트를 만들 수 있습니다 [e].

            [x] --- [y]
           /
[에이 비 씨 디이]

변경 세트 [e]만 푸시하려면 다음을 수행해야합니다.

% hg push -r [e]

[e]변경 세트 해시는 어디에 있습니까 ? 기본적으로 hg push단순히 저장소를 비교하고 그 표시됩니다 [x], [y][e]누락,하지만 당신이 공유하고 싶지 않을 수도 [x][y]아직.

버그 수정이 영향을 미치는 경우이를 기능 분기와 병합하려고합니다.

% hg update [y]
% hg merge

그러면 저장소 그래프가 다음과 같이 보입니다.

            [x] --- [y] ----------- [z]
           / /
[에이 비 씨 디이]

여기서 [z]의 병합이다 [y]하고 [e]. 분기를 버릴 수도 있습니다.

% hg strip [x]

이 이야기의 주요 요점은 이것입니다. 단일 클론은 여러 개발 과정을 쉽게 나타낼 수 있습니다. 이것은 확장명을 사용하지 않고 "평범한 hg"에 항상 적용되었습니다. 하지만 북마크 확장 은 큰 도움이됩니다. 변경 세트에 이름 (책갈피)을 지정할 수 있습니다. 위의 경우 개발 헤드에 북마크를, 업스트림 헤드에 북마크를 추가해야합니다. Mercurial 1.6을 사용하여 책갈피를 밀고 끌 수 있으며 Mercurial 1.8에서 기본 제공 기능이되었습니다.

두 개의 클론을하기로 선택했다면, 개발 클론 한 후이 닮은 것 [x][y]:

[a] --- [b] --- [x] --- [y]

업스트림 클론에는 다음이 포함됩니다.

[a] --- [b] --- [c] --- [d]

이제 버그를 확인하고 수정하십시오. hg update업스트림 클론을 사용할 준비가되었으므로 여기에 있지 않아도됩니다 . 당신은 커밋하고 만듭니다 [e]:

[a] --- [b] --- [c] --- [d] --- [e]

개발 복제본에 버그 수정을 포함 시키려면 다음을 수행하십시오.

[a] --- [b] --- [x] --- [y]
           \
            [c] --- [d] --- [e]

병합 :

[a] --- [b] --- [x] --- [y] --- [z]
           \ /
            [c] --- [d] --- [e]

그래프는 다르게 보일 수 있지만 구조는 동일하지만 최종 결과는 동일합니다. 클론을 사용하면 약간의 정신 부기를해야했습니다.

명명 된 지점은 실제로 선택 사항이므로 여기 그림에 나오지 않았습니다. 머큐리얼 자체는 몇 년 동안 두 개의 클론을 사용하여 개발되었으며 우리는 명명 된 분기를 사용하기로 전환했습니다. 우리는 'default'브랜치 외에도 'stable'이라는 브랜치를 유지하고 'stable'브랜치를 기반으로 릴리스를 만듭니다. 권장 워크 플로우에 대한 설명은 위키 의 표준 분기 페이지를 참조하십시오 .


1
변경 세트가 다른 사용자로부터 온 것이라면 기록되었을 것이므로 복제본을 사용하는 것은 나쁘지 않습니다. 새 기능을 푸시 할 때 별도의 리포지토리에서이 기능을 수행했다는 사실을 이해하지 못하는 경우가 많습니다. 로컬 브랜치 확장 기능도있어 로컬 전용 브랜치를 제공합니다. 리포지토리를 복제 할 때 높은 비용 (시간 / 공간)과 관련이 있습니다.
Johannes Rudolph

2
'복제본은 빠른 실험에 좋습니다'-아니요, 그렇지 않습니다! 저장소에 파일이 너무 많으면 어떻게 되나요? 분기 전환 시간 (<1 초) 동안 복제에는 시간 (1 분 이상)이 소요됩니다. 여전히 명명 된 브랜치를 사용하면 변경 로그가 오염됩니다. 끝이 아닌가? 아니면 뭔가 빠졌습니까?
seler

좋아 셀러; 그의 원래 주장에 대한 수정처럼 들린다; 클론은 여러 개의 완전한 사본의 오버 헤드가 중요하지 않은 경우 또는 hg의 심볼릭 링크 / 하드 링크를 사용하여 지점 당 별도의 로컬 작업 사본 비용을 완화 할 수있는 경우에 좋습니다.
워렌 P

@seler : 코드 배드가 크면 클론이 실용적이지 않다는 것이 옳습니다. 북마크가 해결책입니다.
Martin Geisler

29

하나의 레포로 전체 역사를 원한다고 생각합니다. 단기 리포지토리를 생성하는 것은 출시와 같은 주요 이벤트가 아닌 단기 실험을위한 것입니다.

Mercurial의 실망 중 하나는 단기 지점을 만들고, 놀고, 버리고, 쓰레기를 모으는 쉬운 방법이 없다는 것입니다. 지점은 영원합니다. 나는 역사를 포기하고 싶지 않은 것에 대해 공감하지만, 매우 저렴한 일회용 브랜치는 git내가 실제로보고 싶은 기능이다 hg.


20
이러한 기능 분기를 매우 쉽게 만들 수 있습니다. 분기점에 "hg 업데이트", 편집 및 "hg commit". 다양한 개발 라인을 새로 만들었습니다. 새로운 커밋으로이 지점이 확장됩니다. "hg clone -r"을 사용하여 제거하거나 "hg strip"을 사용하여 인라인을 제거하십시오. 따라서 실망하지 마시고 기능 요청과 함께 Mercurial 메일 링리스트에 오십시오.
Martin Geisler

8
hg strip내가 원하는 것 같습니다 . 온라인 문서 클레임 분기를 삭제할 수없는 이유는 무엇입니까?
Norman Ramsey

11
Mercurial이 git보다 저렴한 지사에 대한 설명은 stevelosh.com/blog/entry/2009/8/30/…
Martin Geisler

9
로 이름이 지정된 분기를 닫을 수 있습니다 hg ci --close-branch.
Andrey Vlasovskikh

3
@Norman Ramsey : 사람들이 분기를 삭제할 수 없다고하면 변경 세트에 포함 된 분기 이름을 변경할 수 없음을 의미합니다. 브랜치 대한 변경 세트 는 브랜치를 정의 하지 않습니다 . 변경 지점을 다른 지점으로 "이동"시키려면 변경 세트를 삭제하고 다른 지점 이름으로 다시 작성해야합니다.
Martin Geisler

14

다해야 합니다 .

@Norman의 대답으로 시작하십시오. 릴리스 당 하나의 지사를 가진 하나의 저장소를 사용하십시오.

그런 다음 구축 및 테스트를 위해 릴리스 지점 당 하나의 복제본을 만듭니다.

하나의 주요 참고 사항은 여러 저장소를 사용하더라도 transplant1) 해시가 변경되고 2) 변경 세트 사이에 충돌하는 변경이있을 때 감지하기 어려운 버그가 발생할 수 있으므로 변경 세트를 이동 하는 데 사용하지 않아야한다는 것입니다 이식과 목표 지점. 대신 일반적인 병합을 수행하려고합니다 (프리 병합없이 : 항상 시각적으로 병합을 검사하십시오).

그래프는 다르게 보일 수 있지만 구조는 동일하지만 최종 결과는 동일합니다.

더 자세하게는, 여러 저장소를 사용하는 경우 "트렁크"저장소 (또는 기본, 기본, 개발 등)는 모든 저장소의 모든 변경 세트를 포함 합니다. 각 릴리스 / 분기 저장소는 단순히 트렁크에서 하나의 브랜치이며, 이전 릴리스를 남겨두고 싶을 때까지 모두 한쪽으로 또는 다른 쪽 트렁크로 다시 병합됩니다. 따라서 명명 된 브랜치 체계에서 해당 기본 리포지토리와 단일 리포지토리의 유일한 차이점은 단순히 브랜치의 이름이 지정되는지 여부입니다.

내가 왜 "하나의 레포로 시작"이라고 말했는지 분명하게 알 수 있습니다. 이 단일 저장소는 모든 릴리스에서 변경 세트 를 찾아야하는 유일한 장소 입니다. 버전 지정을 위해 릴리스 분기에서 변경 세트에 추가로 태그를 지정할 수 있습니다. 개념적으로 명확하고 단순하며 시스템 관리자가 항상 사용 가능하고 항상 복구 할 수 있어야하는 유일한 시스템이기 때문에 더 간단합니다.

그러나 분기 / 릴리스 당 하나의 복제본을 유지 관리해야하며 빌드 및 테스트해야합니다. 당신이 할 수있는 것처럼 사소한 hg clone <main repo>#<branch> <branch repo>일이며 hg pull, 지점 리포지토리에서는 해당 지점의 새로운 변경 세트 (병합 된 이전 지점의 상위 변경 세트) 만 가져옵니다.

이 설정은 단일 풀러 의 Linux 커널 커밋 모델에 가장 적합합니다 ( Lith Linus처럼 행동하는 것이 좋지 않습니다. 우리 회사에서는 역할 통합 자라고 부릅니다 ). 끌어 당기는 사람이 들어와야합니다. 지점 저장소의 유지 관리는 순전히 릴리스 관리를위한 것이며 완전히 자동화 될 수 있습니다. 개발자는 지점 리포지토리로 끌어 당길 필요가 없습니다.


다음은이 설정에 대한 @mg의 예입니다. 출발점:

[a] - [b]

알파 릴리스에 도달하면 릴리스 버전의 이름 지정된 분기 (예 : "1.0")를 작성하십시오. 그것에 대한 버그 수정을 커밋 :

[a] - [b] ------------------ [m1]
         \                 /
          (1.0) - [x] - [y]

(1.0)커밋 할 때까지 명명 된 분기가 없으므로 실제 변경 세트가 아닙니다. (태그 추가와 같은 사소한 커밋을 수행하여 이름이 지정된 분기를 올바르게 만들 수 있습니다.)

병합 [m1]은이 설정의 핵심입니다. 헤드 수가 무제한 인 개발자 리포지토리와 달리 기본 리포지토리에 여러 헤드를두기를 원하지 않습니다 (이전에 언급 한 오래된 데드 릴리스 지점 제외). 따라서 릴리스 분기에 새 변경 세트가있을 때마다 즉시 기본 분기 (또는 이후 릴리스 분기)로 다시 변경해야합니다. 이렇게하면 한 릴리스의 모든 버그 수정이 모든 이후 릴리스에도 포함됩니다.

한편 기본 브랜치의 개발은 다음 릴리스로 계속 진행됩니다.

          ------- [c] - [d]
         /
[a] - [b] ------------------ [m1]
         \                 /
          (1.0) - [x] - [y]

평소와 같이 기본 분기에서 두 헤드를 병합해야합니다.

          ------- [c] - [d] -------
         /                         \
[a] - [b] ------------------ [m1] - [m2]
         \                 /
          (1.0) - [x] - [y]

그리고 이것은 1.0 브랜치 클론입니다 :

[a] - [b] - (1.0) - [x] - [y]

이제 다음 릴리스 브랜치를 추가하는 연습입니다. 그것이 2.0이라면 분명히 기본값에서 분기됩니다. 1.1 인 경우 1.0 또는 기본값을 분기하도록 선택할 수 있습니다. 어쨌든 1.0의 새 변경 세트는 먼저 다음 분기로 병합 된 다음 기본값으로 병합해야합니다. 충돌이없는 경우 자동으로 수행 될 수 있으며 빈 병합 만 발생합니다.


나는 그 예가 나의 이전의 요점들을 분명히하기를 바란다. 요약하면이 방법의 장점은 다음과 같습니다.

  1. 완전한 변경 세트 및 버전 기록이 포함 된 단일 권한 저장소
  2. 명확하고 간단한 릴리스 관리.
  3. 개발자 및 통합자를위한 명확하고 단순화 된 워크 플로우.
  4. 워크 플로우 반복 (코드 검토) 및 자동화 (자동 빈 병합)를 용이하게합니다.

UPDATE hg 자체 가이 작업을 수행합니다 . 기본 리포지토리 에는 기본 및 안정적인 분기가 포함되어 있으며 안정적인 리포지토리 는 안정적인 분기 복제본입니다. 안정적인 브랜치를 따르는 버전 태그는 릴리스 관리 목적으로 충분하기 때문에 버전 브랜치를 사용하지 않습니다.


5

내가 아는 한 가장 큰 차이점은 이미 언급 한 것입니다. branched라는 이름은 단일 저장소에 있습니다. 명명 된 지점은 한 곳에서 모든 것을 편리하게 사용할 수 있습니다. 별도의 저장소는 더 작고 이동하기 쉽습니다. 여기에 두 개의 생각 학교가있는 이유는 확실한 승자가 없기 때문입니다. 어느 쪽의 주장이 당신에게 가장 합리적일까요? 아마도 당신의 환경이 당신과 가장 비슷할 것이므로 아마도 당신이 함께 가야 할 것입니다.


2

현재 상황 (예 : 피처 / 재 설계의 크기)에 따라 실제적인 결정이라고 생각합니다. 나는 아직 커미터가 아닌 역할을 가진 기고자들이 무시할 수없는 기술적 오버 헤드로 적성을 입증함으로써 개발자 팀에 합류하는 데 포크가 정말 좋다고 생각한다.


0

버전에 이름이 지정된 분기를 사용하지 않는 것이 좋습니다. 그것이 실제로 태그의 목적입니다. 지명 된 지점은 stable지점 과 같이 오래 지속되는 전환을 의미합니다 .

그렇다면 왜 태그를 사용하지 않습니까? 기본 예 :

  • 단일 지점에서 개발
  • 릴리스가 작성 될 때마다 그에 따라 태그를 지정합니다
  • 거기서부터 개발은 계속됩니다
  • 특정 릴리스에서 수정해야 할 버그가 있거나 태그로 업데이트하고 변경 한 후 커밋하기 만하면됩니다.

그러면 default분기 에 이름없는 새 헤드가 생성됩니다 . 익명의 브랜치는 hg에서 완벽하게 좋습니다. 그러면 언제든지 버그 수정 커밋을 기본 개발 트랙으로 병합 할 수 있습니다. 명명 된 지점이 필요하지 않습니다.


이것은 프로세스에 따라 크게 달라집니다. 예를 들어, 웹 앱은 안정적인 / 테스트 / 개발 분기 계층 구조와 잘 작동합니다. 데스크톱 소프트웨어를 구축 할 때 일반적으로 개발 지점 (기본값)과 유지 관리에 1 ~ 3 개의 서로 다른 지점이 있습니다. 언제 브랜치를 다시 방문해야하는지 예측하기가 어렵고 브랜치가 major.minor 버전을 추적하는 것에 대한 특정 우아함이 있습니다.
James Emerton
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.