programing

병합: Hg/Git 대 SVN

oldcodes 2023. 9. 1. 21:19
반응형

병합: Hg/Git 대 SVN

저는 종종 Hg(및 Git 및...)가 SVN보다 병합을 더 잘한다는 것을 읽지만 SVN이 실패하는 경우(또는 SVN이 수동 개입이 필요한 경우) Hg/Git가 병합할 수 있는 곳에 대한 실제 사례는 본 적이 없습니다.Hg/Git가 순조롭게 진행되는 동안 SVN이 어디에서 실패하는지 보여주는 지점/수정/커밋/...-작업의 단계별 목록을 몇 개 게시해 주시겠습니까?아주 예외적이지는 않은 실용적인 경우를 부탁드립니다.

몇 가지 배경: 수십 명의 개발자가 SVN을 사용하여 프로젝트를 진행하고 있으며 각 프로젝트(또는 유사한 프로젝트 그룹)는 자체 저장소에 있습니다.릴리스 및 기능 분기를 적용하여 문제가 자주 발생하지 않도록 하는 방법을 알고 있습니다(예: 우리는 그곳에 가본 적이 있지만 "한 프로그래머가 팀 전체에 트라우마를 일으키거나" 또는 "지점을 다시 통합하기 위해 2주 동안 6명의 개발자가 필요합니다"라는 Joel의 문제를 극복하는 방법을 배웠습니다.매우 안정적이고 버그 수정을 적용하는 데만 사용되는 릴리스 분기가 있습니다.우리는 일주일 안에 출시할 수 있을 정도로 안정적이어야 하는 트렁크를 가지고 있습니다.그리고 단일 개발자 또는 개발자 그룹이 작업할 수 있는 기능 부서가 있습니다.예, 재통합 후 삭제되므로 저장소가 혼란스럽지 않습니다. ;)

그래서 SVN에 비해 Hg/Git의 장점을 찾고 있습니다.저는 실제 경험을 쌓고 싶지만, Hg/Git로 옮길 수 있는 더 큰 프로젝트가 아직 없기 때문에, 저는 몇 개의 합성 파일만 포함된 작은 인공 프로젝트를 가지고 노는 것에 얽매여 있습니다.그리고 저는 Hg/Git의 인상적인 힘을 느낄 수 있는 몇 가지 사례를 찾고 있습니다. 왜냐하면 지금까지 저는 그것들에 대해 자주 읽었지만 직접 찾지 못했기 때문입니다.

저도 서브버전이 지점을 합병하는 데 실패하고 머큐리얼(그리고 깃, 바자 등)이 옳은 일을 하는 경우를 찾고 있었습니다.

SVN Book이름이 변경된 파일이 잘못 병합되는 방법을 설명합니다.이는 Subversion 1.5, 1.6, 1.7 및 1.8에 적용됩니다! 아래의 상황을 재현하려고 했습니다.

cd /tmprm -rf svn-reposvn-modulesvnadmin 생성 svn-reposvn checkout file://tmp/svn-reposvn-bulletcd svn 매개 변수mkdir 트렁크 가지안녕, 세상이여!' > 트렁크/안녕하세요.txtsvn 트렁크 분기 추가svn commit -m '초기 가져오기'입니다.svn copy '^/trunk' '^/branchs/rename' -m 'branch 만들기'.svn 스위치 '^/switch'.에코 'Hello, World!' > 안녕하세요.txtsvn commit -m '트렁크에서 업데이트'.svn 스위치 '^/sigma/sigma'.svn 이름 변경 hello.txt hello.en.txtsvn commit -m '지점의 이름 바꾸기'.svn 스위치 '^/switch'.svn merge --'^/sigma/sigma'를 다시 통합합니다.

, 완료되어야 , 에책따병깨합완끗게하업하료이다있야 업데이트 가 포함되어 .trunk충돌이 합니다(은 작성 1대신 트리 충돌이 발생합니다(이것은 작성 당시 데비안의 최신 버전인 서브버전 1.6.17).

리포지토리 URL 간의 차이를 '.'로 병합:A hello.en.txt안녕하세요.txt충돌 요약:
트리 충돌: 1

충돌이 없어야 합니다. 업데이트를 파일의 새 이름으로 병합해야 합니다.서브버전이 실패하는 동안 Mercurial은 다음을 올바르게 처리합니다.

rm -rf /tmp/hg-repo
hg init /tmp/hg-repo
cd /tmp/hg-repo
echo 'Goodbye, World!' > hello.txt
hg add hello.txt
hg commit -m 'Initial import.'
echo 'Hello, World!' > hello.txt
hg commit -m 'Update.'
hg update 0
hg rename hello.txt hello.en.txt
hg commit -m 'Rename.'
hg merge

병합하기 전에 저장소는 다음과 같이 표시됩니다.hg glog):

변경 세트: 2:6502899164cc태그: 팁상위: 0:d08pxbadd9e사용자: Martin Geisler날짜:4월 1일 목요일 12:29:19 2010 +0200summary: 이름 변경.
|o 변경 세트: 1:9d06fa155634사용자: Martin Geisler날짜:4월 1일 목요일 12:29:18 2010 +0200요약: 업데이트.
|o changeset : 0:d08pxbadd9e사용자: Martin Geisler날짜:4월 1일 목요일 12:29:18 2010 +0200요약:.초기 가져오기.

병합의 출력은 다음과 같습니다.

hello.en.txt와 hello를 병합합니다.txt to hello.en.txt0개 파일 업데이트, 1개 파일 병합, 0개 파일 제거, 0개 파일 확인 안 됨(병렬 병합, 커밋하는 것을 잊지 마십시오)

말로는: 다, 음과같다니습즉다▁in.2(Mercurial 개 1에서변에사 2정다서새이통 2니습파합했름로일으정은와에▁▁name다니통m▁file▁new▁from▁took습▁the▁(▁and▁it▁2▁1했에▁revision▁into합▁merged▁from▁the▁change▁revisionhello.en.txt물론 리팩터링과 리팩터링을 지원하기 위해서는 이 사례를 다루는 것이 꼭 필요합니다. 바로 지점에서 하고 싶은 일입니다.

저는 서브버전을 직접 사용하지는 않지만 서브버전 1.5: 병합 추적(기초)의 릴리스 노트를 보면 Git 또는 Mercurial과 같은 전체 DAG 버전 제어 시스템에서 병합 추적이 작동하는 방식과 다음과 같은 차이점이 있는 것 같습니다.

  • 트렁크에서 분기로 병합하는 것은 분기에서 트렁크로 병합하는 것과 다릅니다. 어떤 이유로 트렁크에서 분기로 병합하는 것이 필요합니다.--reintegrate에 대한 선택권.svn merge.

    Git 또는 Mercurial과 같은 분산 버전 제어 시스템에서는 트렁크와 브랜치 사이에 기술적 차이가 없습니다. 모든 브랜치는 동일하게 생성됩니다(하지만 사회적 차이가 있을 수도 있습니다).어느 방향으로든 병합은 동일한 방식으로 수행됩니다.

  • 은 새로운 새제을야합니를 .-g(--use-merge-history할 수 없습니다.svn log그리고.svn blame병합 추적을 고려합니다.

    Git 및 Mercurial 병합 추적은 기록(로그) 및 책임을 표시할 때 자동으로 고려됩니다.Git에서 첫 번째 부모를 따르도록 요청할 수 있습니다.--first-parent( 것 .)에서 병합 를 " 위해 Mercurial에도 유사한 옵션이 있습니다.git log.

  • 내가 이해한 바로는svn:mergeinfo속성은 충돌에 대한 경로별 정보를 저장하는 반면, Git 및 Mercurial에서는 단순히 둘 이상의 부모를 가질 수 있는 개체를 커밋합니다.

  • 하위 버전의 병합 추적에 대한 "알려진 문제" 하위 섹션은 반복적/주기적/반영적 병합이 제대로 작동하지 않을 수 있음을 시사합니다.즉, 다음 기록에서 두 번째 병합은 올바른 작업을 수행하지 못할 수 있습니다('A'는 트렁크 또는 분기일 수 있고 'B'는 각각 분기 또는 트렁크일 수 있습니다).

    *---*x---*y---***---M2 <---A\       \             /--*----M1---*---**/ <--B

    위의 ASCII-art가 깨지는 경우: 'x' 버전에서 분기 'A'에서 분기 'B'가 생성(포킹)된 다음, 이후의 분기 'A'가 수정 버전 'y'에서 병합 'M1'로 분기 'B'로 병합되고, 마지막으로 분기 'B'가 병합 'M2'로 병합됩니다.

    *---*x---*---M1---*---*M2 <---A\       /           /\-*---y---**/ <---B

    상기 ASCII-art가 깨지는 경우: 'x'번에서 분기 'A'번에서 분기 'B'번이 생성(포킹)되고, 'y'번에서 분기 'A'번으로 병합되고, 이후 다시 분기 'A'번으로 병합됩니다.

  • 하위 버전은 교차 병합의 고급 사례를 지원하지 않을 수 있습니다.

    *---b-----B1---M1---*---M3\     \ /        /X /\   / \      /\--B2--M2--*

    Git는 실제로 "재귀적" 병합 전략을 사용하여 이 상황을 잘 처리합니다.머큐리얼에 대해서는 잘 모르겠습니다.

  • "알려진 문제"에서 병합 추적이 파일 이름 변경(및 수정)과 함께 작동하지 않을 수 있다는 경고가 표시됩니다. 예를 들어, 한 쪽에서는 파일 이름을 변경(및 수정)하고 다른 쪽에서는 파일 이름을 변경(이전 이름으로)하지 않고 파일을 수정합니다.

    Git와 Mercurial 둘 다 실제로 이러한 사건을 잘 처리합니다.이름 바꾸기 탐지를 사용하여 Git, 이름 바꾸기 추적을 사용하여 Mercurial.

HTH

일반적인 이점(오프라인 커밋, 게시 프로세스 등)에 대해서는 언급하지 않고 다음과 같이 "합병" 예를 들어보겠습니다.

제가 계속해서 보는 주요 시나리오는... 관련이 없는 두 가지 작업이 실제로 개발되는 지점입니다.
(하나의 기능에서 시작되었지만 이 다른 기능의 개발로 이어졌습니다.
또는 패치에서 시작되었지만 다른 기능의 개발로 이어짐).

메인 분기에서 두 기능 중 하나만 병합하는 방법은 무엇입니까?
또는 두 기능을 분기별로 어떻게 분리합니까?

패치를 생성하려고 시도할 수도 있습니다. 문제는 다음과 같은 기능 종속성을 더 이상 확신할 수 없다는 것입니다.

  • 패치에 사용된 커밋(또는 SVN에 대한 리비전)
  • 패치의 일부가 아닌 다른 커밋

Git (그리고 Mercurial도 마찬가지인 것 같습니다) rebase --to 옵션은 분기의 rebase(루트 재설정) 부분을 제안합니다.

제프로미의 게시물에서

- x - x - x (v2) - x - x - x (v2.1)
           \
            x - x - x (v2-only) - x - x - x (wss)

v2용 패치와 새로운 wss 기능이 있는 이 상황을 다음과 같이 해결할 수 있습니다.

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - x - x (v2-only)
           \
             x - x - x (wss)

다음을 허용:

  • 각 분기를 분리하여 테스트하여 모든 항목이 의도한 대로 컴파일/작동하는지 확인
  • 유지 관리할 항목만 병합합니다.

제가 좋아하는 또 다른 기능(합병에 영향을 미치는 기능)은 다음을 제시하기 위해 커밋을 (아직 다른 레포로 푸시되지 않은 분기에서) 스쿼시할있는 기능입니다.

  • 더 깨끗한 역사
  • 더 일관성 있는 커밋(commit1은 function1, commit2는 function2, commit3은 function1...)

이를 통해 훨씬 더 쉽고 충돌이 적은 병합을 보장할 수 있습니다.

우리는 최근 SVN에서 GIT로 이전했고, 이와 같은 불확실성에 직면했습니다.GIT가 더 낫다는 일화적인 증거가 많았지만, 어떤 예도 접하기 어려웠습니다.

하지만 GIT가 SVN보다 합병을 훨씬 더 잘한다는 것을 말씀드릴 수 있습니다.이것은 분명히 일화적이지만 따라야 할 표가 있습니다.

다음은 당사가 발견한 몇 가지 사항입니다.

  • SVN은 그렇게 해서는 안 될 것 같은 상황에서 많은 나무 충돌을 토하곤 했습니다.우리는 결코 이 문제의 진상을 규명하지 못했지만 GIT에서는 그런 일이 일어나지 않았습니다.
  • GIT는 더 낫지만 훨씬 더 복잡합니다.훈련에 시간을 보내세요.
  • 우리는 우리가 좋아하는 거북이 SVN에 익숙했습니다.거북이 GIT는 그렇게 좋지 않습니다. 이것은 당신을 실망시킬지도 모릅니다.하지만 저는 이제 Torothy SVN이나 GIT GUI 중 어느 것보다 훨씬 선호하는 GIT 명령줄을 사용합니다.

GIT를 평가할 때 다음과 같은 테스트를 수행했습니다.이것들은 합병에 관한 한 GIT가 승자라는 것을 보여주지만, 그렇게 많지는 않습니다.실제로는 그 차이가 훨씬 더 크지만, SVN이 잘못 처리하는 상황을 복제하지는 못한 것 같습니다.

GIT vs SVN Merging Evaluation

다른 사람들은 이것의 더 이론적인 측면을 다루었습니다.좀 더 실용적인 관점을 빌려줄 수 있을 것 같습니다.

저는 현재 "기능 지사" 개발 모델에서 SVN을 사용하는 회사에서 일하고 있습니다.즉, 다음과 같습니다.

  • 트렁크에 대한 작업을 수행할 수 없습니다.
  • 각 개발자는 자신만의 지점을 만들 수 있습니다.
  • 분기는 수행된 작업 기간 동안 유지되어야 합니다.
  • 각 작업에는 고유한 분기가 있어야 합니다.
  • 트렁크에 다시 병합할 권한을 부여해야 합니다(일반적으로 bugzilla를 통해).
  • 높은 수준의 제어가 필요한 경우 게이트키퍼가 병합할 수 있습니다.

일반적으로 효과가 있습니다.SVN은 이런 흐름에 사용될 수 있지만 완벽하지 않습니다.SVN에는 방해가 되고 인간의 행동을 형성하는 몇 가지 측면이 있습니다.그것은 몇 가지 부정적인 측면을 줍니다.

  • 우리는 사람들이 더 낮은 지점에서 분기하는 것에 대해 꽤 많은 문제를 겪었습니다.^/trunk이렇게 하면 트리 전체에 걸쳐 정보 레코드가 병합되고 병합 추적이 중단됩니다.거짓 갈등이 나타나기 시작하고, 혼란이 지배합니다.
  • 트렁크에서 분기로 변경 사항을 수집하는 것은 비교적 간단합니다.svn merge당신이 원하는 것을 합니다.변경 사항을 다시 병합하려면 다음 사항이 필요합니다(알림).--reintegrate됩니다.이 스위치를 제대로 이해한 적은 없지만, 다시는 분기를 트렁크에 통합할 수 없다는 뜻입니다.라는 것을 (), 중된분기므참계면려새속만야합들어니다. (노트 참조)
  • 지점을 만들고 삭제할 때 URL을 통해 서버에서 작업을 수행하는 전체 업무는 정말로 사람들을 혼란스럽게 하고 두렵게 합니다.그래서 그들은 그것을 피합니다.
  • 분기 간 전환은 트리의 일부는 분기 A를 보고 다른 일부는 분기 B를 보고 오류가 발생하기 쉽습니다.그래서 사람들은 모든 일을 한 지점에서 하는 것을 선호합니다.

엔지니어는 1일차에 지점을 만드는 경향이 있습니다.그는 그의 일을 시작하고 그것에 대해 잊어버립니다.얼마 후 한 상사가 와서 트렁크에 작업을 맡길 수 있는지 물어봅니다.엔지니어는 재통합이 다음을 의미하기 때문에 이 날을 두려워해 왔습니다.

  • 오랫동안 살았던 그의 브랜치를 다시 트렁크에 통합하고 모든 충돌을 해결하고, 별도의 브랜치에 있어야 했지만 그렇지 않았던 관련 없는 코드를 릴리스했습니다.
  • 분기 삭제
  • 새 분기 생성
  • 작업 복사본을 새 지점으로 전환하는 중

엔지니어는 이 작업을 가능한 한 적게 수행하기 때문에 각 단계를 수행해야 하는 "순수한 주문"을 기억하지 못합니다.잘못된 스위치와 URL이 발생하고, 갑자기 엉망이 되어 "전문가"를 구하러 갑니다.

결국 모든 것이 정착되고, 사람들은 단점을 다루는 법을 배우지만, 새로운 출발자들은 각각 같은 문제를 겪습니다.궁극적인 현실은 (처음에 제시한 것과는 반대로) 다음과 같습니다.

  • 트렁크에 대한 작업이 수행되지 않습니다.
  • 각 개발자는 하나의 주요 지점을 가집니다.
  • 분기는 작업을 해제해야 할 때까지 지속됩니다.
  • 티켓된 버그 수정은 자체 분기를 갖는 경향이 있습니다.
  • 승인된 경우 트렁크에 다시 병합됩니다.

...그렇지만.....

  • 때때로 일은 다른 것과 같은 지점에 있기 때문에 그렇게 해서는 안 될 때 그것을 트렁크로 만듭니다.
  • 사람들은 모든 병합(심지어 쉬운 것)을 피하므로 종종 자신의 작은 거품에서 작업합니다.
  • 대규모 합병이 발생하는 경향이 있으며, 제한된 양의 혼란을 야기합니다.

다행히도 그 팀은 대처할 수 있을 만큼 작지만, 규모가 확장되지는 않을 것입니다.중요한 것은 CVCS에는 문제가 없다는 것입니다. 하지만 합병은 DVCS만큼 중요하지 않기 때문에 그렇게 매끄럽지는 않습니다.이러한 "합병 마찰"로 인해 동작이 발생하며, 이는 "피쳐 분기" 모델이 고장나기 시작함을 의미합니다.올바른 병합은 DVCS뿐만 아니라 모든 VCS의 기능이어야 합니다.


이것에 따르면, 이제는.--record-only문제를 해결하는 데 사용할 수 있는 스위치--reintegrate문제가 있습니다. v1.8은 재통합을 자동으로 수행할 시기를 선택하며, 이후 분기가 중단되지 않습니다.

버전 1.5 이전(내가 잘못 알고 있는 것이 아니라면)에는 병합 기록을 기억하지 못한다는 점에서 상당한 단점이 있었습니다.

VonC가 요약한 사례를 살펴보겠습니다.

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - A - x (v2-only)
           \
             x - B - x (wss)

개정 A 및 B에 유의하십시오."wss" 분기의 리비전 A에서 "v2-only" 분기로 변경 사항을 병합했지만(어떤 이유로든) 두 분기를 계속 사용했다고 가정합니다.머큐리얼을 사용하여 두 분기를 다시 병합하려고 하면 수정 버전 A와 B 이후에만 변경 사항이 병합됩니다.이전에 병합하지 않은 것처럼 모든 것을 병합해야 합니다.

이것은 B에서 A로의 병합이 코드의 양 때문에 몇 시간이 걸렸던 저의 경험에서 나온 예입니다. 1.5 이전 버전의 경우와 마찬가지로 다시 겪어야 하는 진짜 고통이었을 것입니다.

Hginit과의 병합 동작에서 또 다른, 아마도 더 관련성 있는 차이: 버전 재교육:

여러분과 제가 어떤 코드를 연구하고 있다고 상상해보세요. 그리고 우리는 코드를 분기하고, 각자의 작업 공간으로 가서 그 코드에 많은 변화를 주고, 그래서 그들은 꽤나 많은 차이를 보였습니다.

우리가 병합해야 할 때, Subversion은 수정된 나의 코드와 당신의 수정된 코드 두 개의 리비전을 보고 어떻게 그것들을 하나의 큰 엉망진창으로 만들 것인지 추측하려고 합니다.일반적으로 실패하고, 실제로 충돌하지 않는 "합병 충돌" 페이지와 페이지를 생성합니다. 단순히 서브버전이 우리가 무엇을 했는지 파악하지 못한 곳입니다.

대조적으로, 우리가 머큐리알에서 따로 일하는 동안 머큐리알은 일련의 변경 사항을 보관하느라 바빴습니다.그래서, 우리가 코드를 함께 병합하고 싶을 때, 머큐리얼은 실제로 훨씬 더 많은 정보를 가지고 있습니다. 그것은 우리 각자가 무엇을 바꿨는지 알고 있고, 그 변화를 다시 적용할 수 있습니다. 최종 제품을 보고 어떻게 조합할지 추측하는 것이 아니라요.

간단히 말해서, Mercurial의 차이 분석 방법은 (?) 전복의 방법보다 뛰어났습니다.

언급URL : https://stackoverflow.com/questions/2475831/merging-hg-git-vs-svn

반응형