병합 : Hg / Git vs. SVN Git이 행복하게 진행되는 동안

나는 종종 Hg (및 Git 및 …)가 SVN보다 병합하는 것이 더 낫다는 것을 읽었지만 SVN이 실패한 곳 (또는 SVN이 수동 개입이 필요한 곳)을 Hg / Git이 병합 할 수있는 실제적인 예를 본 적이 없습니다. Hg / Git이 행복하게 진행되는 동안 SVN이 실패하는 위치를 나타내는 분기 / 수정 / 커밋 / … 작업의 몇 가지 단계별 목록을 게시 할 수 있습니까? 실용적이고 예외적 인 사례는 아닙니다.

일부 배경 : SVN을 사용하여 프로젝트를 수행하는 수십 명의 개발자가 있으며, 각 프로젝트 (또는 유사한 프로젝트 그룹)는 자체 저장소에 있습니다. 릴리스 및 기능 분기를 적용하는 방법을 알고 있으므로 문제가 자주 발생하지 않습니다 (예 : 우리는 그곳에 있었지만 “한 명의 프로그래머가 팀 전체에 외상을 일으킴”) 의 Joel의 문제 를 극복하는 방법을 배웠습니다. 또는 “지사를 재 통합하기 위해 2 주 동안 6 명의 개발자가 필요”). 릴리스 브랜치는 매우 안정적이며 버그 수정을 적용하는 데만 사용됩니다. 일주일 이내에 릴리스를 만들 수있을만큼 안정적인 트렁크가 있습니다. 또한 단일 개발자 또는 개발자 그룹이 작업 할 수있는 기능 분기가 있습니다. 예, 재 통합 후 삭제되므로 리포지토리가 복잡해지지 않습니다. 😉

그래서 나는 여전히 SVN에 비해 Hg / Git의 장점을 찾으려고 노력하고 있습니다. 실습 경험을 갖고 싶지만 아직 Hg / Git으로 옮길 수있는 더 큰 프로젝트가 없기 때문에 몇 개의 구성 파일 만 포함 된 작은 인공 프로젝트를 계속 사용하고 있습니다. 그리고 나는 Hg / Git의 인상적인 힘을 느낄 수있는 몇 가지 사례를 찾고 있습니다. 지금까지 나는 그것에 대해 자주 읽었지만 스스로 찾지 못했습니다.



답변

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

  • 트렁크 간 병합은 분기 간 병합과 다릅니다. 어떤 이유로 트렁크 간 병합에는 --reintegrate옵션이 필요 합니다 svn merge.

    힘내이나 의욕과 같은 분산 버전 관리 시스템에서 더이없는 기술 모든 지점이 (가있을 수 있습니다 평등하게 창조 : 트렁크 및 지점의 차이 , 사회적 차이, 비록). 어느 방향 으로든 병합하는 방법도 동일합니다.

  • 당신은 새로운 제공해야합니다 -g( --use-merge-history에) 옵션 svn logsvn blame계정으로 병합 추적을 할 수 있습니다.

    Git 및 Mercurial 병합 추적은 기록 (로그) 및 책임을 표시 할 때 자동으로 고려됩니다. Git에서는에 --first-parent머지 트래킹 정보를 “파기”하기 위해 첫 번째 부모 만 따르도록 요청할 수 있습니다 (머큐리얼에도 비슷한 옵션이 있다고 생각합니다) git log.

  • 내가 이해 한 것에서 svn:mergeinfo속성은 충돌에 대한 경로 당 정보를 저장합니다 (Subversion은 변경 세트 기반) .Git 및 Mercurial에서는 단순히 둘 이상의 부모를 가질 수있는 객체를 커밋합니다.

  • Subversion의 병합 추적에 대한 “알려진 문제” 하위 섹션에서는 반복 / 순환 / 반사 병합이 제대로 작동하지 않을 수 있음을 제안합니다. 이는 다음과 같은 이력으로 두 번째 병합이 올바른 작업을 수행하지 않을 수 있음을 의미합니다 ( ‘A’는 트렁크 또는 분기 일 수 있고 ‘B’는 각각 분기 또는 트렁크 일 수 있음).

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

    위의 ASCII 아트가 깨지는 경우 : 개정판 ‘x’에서 분기 ‘A’에서 분기 ‘B’가 생성 (포크) 된 후 나중에 분기 ‘A’가 개정 ‘y’에서 분기 ‘B’로 병합됩니다. ‘M1’을 병합하고 마지막으로 ‘B’분기를 ‘M2’병합으로 ‘A’분기에 병합합니다.

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

    위의 ASCII 아트가 깨지는 경우 : 개정판 ‘x’에서 분기 ‘A’에서 분기 ‘B’가 작성 (포크)되고 ‘y’에서 분기 ‘A’에 ‘M1’으로 병합됩니다. ‘A2’지점에 ‘M2’로 다시 병합되었습니다.

  • Subversion은 십자 병합 의 고급 사례를 지원하지 않을 수 있습니다 .

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

    Git은 실제로 “재귀”병합 전략을 사용하여이 상황을 잘 처리합니다. 나는 Mercurial에 대해 확신하지 못한다.

  • 에서 “알려진 문제” 한쪽 (옛 이름)의 이름을 변경하지 않고 파일 (아마도 수정 IT), 및 제 2 측면 수정 파일의 이름을 변경하는 경우 파일 이름 변경, 예와 migh하지 작업을 추적하는 병합이 경고한다.

    Git과 Mercurial은 실제로 이러한 경우를 잘 처리합니다. 이름 바꾸기 감지를 사용하는 Git , 이름 바꾸기 추적을 사용하는 Mercurial .

HTH


답변

나도 Subversion이 브랜치를 병합하지 못하고 Mercurial (및 Git, Bazaar 등)이 옳은 일을하는 경우를 찾고 있습니다.

SVN Book 이름이 바뀐 파일이 어떻게 잘못 병합 되는지 설명합니다 . 이것은 Subversion 1.5 , 1.6 , 1.71.8에 적용됩니다 ! 아래 상황을 재현하려고했습니다.

CD / tmp
RM - RF SVN - REPO SVN - 체크 아웃
svnadmin svn 만들기 - 저장소
svn 체크 아웃 파일 : /// tmp / svn - repo svn - checkout
CD SVN - 체크 아웃
mkdir 트렁크 브랜치
에코 '안녕, 세상!' > 트렁크 / 안녕하세요 . txt 
svn 트렁크 분기 추가
svn commit - m '초기 가져 오기.' 
svn copy '^ / trunk' '^ / branches / rename' - m '분기 생성' 
svn 스위치 '^ / trunk' . 
에코 '안녕하세요, 세계!' > 안녕하세요 . txt    
svn commit - m '트렁크에서 업데이트.' 
svn 스위치 '^ / branches / rename' . 
svn 이름을 hello로 바꿉니다 . txt hello . ko . txt 
svn commit - m '브랜치 이름 바꾸기' 
svn 스위치 '^ / trunk' . 
SVN 병합 - 재 통합 '^ / 가지 / 이름 바꾸기' 

이 책에 따르면, 병합은 깨끗하게 완료되지만 업데이트 trunk가 잊혀진 이후 이름이 바뀐 파일에 잘못된 데이터가 있어야합니다 . 대신 트리 충돌이 발생합니다 (작성 당시 데비안의 최신 버전 인 Subversion 1.6.17과 관련이 있습니다).

--- 리포지토리 URL의 차이점을 '.'으로 병합 :
hello.en.txt
   C hello.txt
충돌 요약 :
  나무 갈등 : 1

전혀 충돌이 없어야합니다. 업데이트는 파일의 새 이름으로 병합되어야합니다. Subversion은 실패하지만 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

병합하기 전에 저장소는 다음과 같습니다 (from hg glog).

@ changeset : 2 : 6502899164cc
| 태그 : 팁
| 부모 : 0 : d08bcebadd9e
| 사용자 : Martin Geisler
| 날짜 : Thu Apr 01 12:29:19 2010 +0200
| 요약 : 이름을 바꿉니다.
|
| o 변경 세트 : 1 : 9d06fa155634
| / 사용자 : Martin Geisler
| 날짜 : Thu Apr 01 12:29:18 2010 +0200
| 요약 : 업데이트.
|
변경 세트 : 0 : d08bcebadd9e
   사용자 : Martin Geisler
   날짜 : Thu Apr 01 12:29:18 2010 +0200
   요약 : 최초 수입.

병합 결과는 다음과 같습니다.

hello.en.txt와 hello.txt를 hello.en.txt에 병합
0 개 파일 업데이트, 1 개 파일 병합, 0 개 파일 제거, 0 개 파일 미해결
(분기 병합, 커밋하는 것을 잊지 마십시오)

즉, Mercurial은 개정 1에서 변경된 사항을 개정 2 ( hello.en.txt) 의 새 파일 이름으로 병합했습니다 . 이 사건을 처리하는 것은 지원 리팩토링하기 위해 코스 필수적이며 리팩토링은 정확하게 당신이 지점에하고 싶은 것입니다 물건의 종류.


답변

일반적인 이점 (오프라인 커밋, 게시 프로세스 등) 에 대해 말하지 않고 여기에 내가 좋아하는 “병합”예가 있습니다.

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

기본 지점에서 두 기능 중 하나만 병합하는 방법은 무엇입니까?
또는 두 가지 기능을 자체 분기에서 어떻게 분리합니까?

어떤 종류의 패치를 생성하려고 시도 할 수 있습니다. 그 문제는 다음 사이에 존재할 수있는 기능적 종속성 이 더 이상 확실하지 않다는 것입니다 .

  • 패치에 사용 된 커밋 (또는 SVN 개정)
  • 다른 하나는 패치의 일부가 아닌 커밋

힘내 (그리고 Mercurial도 생각합니다) rebase –onto 옵션을 제안하여 브랜치의 일부를 rebase (지점의 루트를 재설정)하십시오.

에서 Jefromi의 게시물

- 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)

을 통해 다음을 수행 할 수 있습니다.

  • 모든 분기가 독립적으로 테스트되어 모든 것이 의도대로 컴파일 / 작동하는지 확인하십시오.
  • 원하는 메인 만 병합하십시오.

내가 좋아하는 다른 기능 (병합에 영향을 미침)은 커밋스쿼시 하여 (아직 다른 리포지토리로 푸시하지 않은 지점에서) 제시하는 기능입니다.

  • 더 깨끗한 역사
  • 더 일관된 커밋

이렇게하면 충돌이 줄고 병합이 훨씬 쉬워집니다.


답변

우리는 최근 SVN에서 GIT로 마이그레이션했으며이 같은 불확실성에 직면했습니다. GIT가 더 낫다는 일화적인 증거가 많이 있었지만 어떤 사례도 다루기가 어려웠습니다.

그래도 GIT는 SVN보다 합병이 훨씬 낫습니다. 이것은 분명히 일화 적이지만 따라야 할 표가 있습니다.

우리가 찾은 것들 중 일부는 다음과 같습니다.

  • SVN은 그렇지 않은 것처럼 보이는 상황에서 많은 트리 충돌을 일으켰습니다. 우리는 이것의 바닥에 결코 도달하지 못했지만 GIT에서는 발생하지 않습니다.
  • 더 나은 반면, GIT는 훨씬 더 복잡합니다. 훈련에 시간을 보내십시오.
  • 우리는 SVN을 Tortoise하는 데 익숙했습니다. 거북이 GIT가 좋지 않아 이로 인해 벗어날 수 있습니다. 그러나 이제는 Tortoise SVN 또는 GIT GUI를 선호하는 GIT 명령 줄을 사용합니다.

GIT를 평가할 때 다음 테스트를 수행했습니다. 이것들은 합병에있어서 GIT를 승자로 표시하지만 그렇게 많이는 아닙니다. 실제로 그 차이는 훨씬 크지 만 SVN이 잘못 처리하는 상황을 재현하지 못했습니다.

GIT vs SVN 병합 평가


답변

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

저는 현재 “피처 지점”개발 모델에서 SVN을 사용하는 회사에서 일하고 있습니다. 그건:

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

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

  • 보다 낮은 지점에서 분기하는 사람들에게는 꽤 많은 문제가있었습니다 ^/trunk. 이 쓰레기는 트리 전체에 정보 레코드를 병합하고 결국 병합 추적을 중단합니다. 거짓 갈등이 나타나고 혼란이 다스립니다.
  • 트렁크에서 지점으로 변경 사항을 가져 오는 것은 비교적 간단합니다. svn merge당신이 원하는 것을합니다. 변경 사항을 다시 --reintegrate병합하려면 merge 명령 이 필요합니다 . 나는이 스위치를 정말로 이해하지 못했지만 지점을 다시 트렁크에 병합 할 수 없음을 의미합니다. 이것은 죽은 지점이므로 작업을 계속하려면 새 지점을 만들어야한다는 것을 의미합니다. (참고 참조)
  • 브랜치를 생성하고 삭제할 때 URL을 통해 서버에서 작업을 수행하는 전체 업무는 사람들을 혼란스럽게하고 두려워합니다. 그래서 그들은 피합니다.
  • 분기 사이를 전환하는 것은 잘못되기 쉬우므로 분기 A를 보는 나무의 일부는 남기고 분기 B를 보는 다른 부분은 남겨 두어야합니다. 따라서 사람들은 한 분기에서 모든 작업을 수행하는 것을 선호합니다.

발생하는 경향은 엔지니어가 1 일에 지점을 만드는 것입니다. 그는 자신의 작업을 시작하고 잊어 버렸습니다. 얼마 후 상사가 와서 자신의 작품을 트렁크에 풀어줄 수 있는지 묻습니다. 재 통합은 다음을 의미하기 때문에 엔지니어는 오늘 두려워하고 있습니다.

  • 오래 살았던 지점을 트렁크로 다시 병합하고 모든 충돌을 해결하고 별도의 지점에 있었지만 관련되지 않은 관련되지 않은 코드를 공개했습니다.
  • 그의 지점을 삭제
  • 새로운 지점 만들기
  • 작업 사본을 새 지점으로 전환

… 엔지니어가 가능한 적은이 작업을 수행하기 때문에 각 단계를 수행하는 “마법의 주문”을 기억할 수 없습니다. 잘못된 스위치와 URL이 발생하면 갑자기 혼란에 빠지고 “전문가”가됩니다.

결국 모든 것이 해결되고 사람들은 단점을 해결하는 방법을 배우지 만 새로운 스타터는 모두 같은 문제를 겪습니다. 최종 현실 (내가 시작했을 때와 반대되는)은 다음과 같습니다.

  • 트렁크에 대한 작업이 없습니다
  • 각 개발자는 하나의 주요 지점을 가지고 있습니다
  • 작업을 해제해야 할 때까지 지점이 지속됩니다.
  • 티켓 버그 수정은 자체 브랜치를 얻는 경향이 있습니다.
  • 승인되면 트렁크로 다시 병합

…그러나…

  • 때로는 작업이 다른 것과 같은 지점에 있기 때문에 트렁크에 들어 가지 않아야합니다.
  • 사람들은 모든 병합 (쉽게 쉬운 것)을 피하므로 사람들은 종종 자신의 작은 거품으로 작업합니다.
  • 큰 병합은 발생하는 경향이 있으며 혼돈을 제한합니다.

고맙게도 팀은 대처하기에 충분히 작지만 확장 할 수 없습니다. 이것은 CVCS에는 아무런 문제가 없지만 DVCS만큼 병합이 중요하지 않기 때문에 매끄럽지 않다는 것입니다. “마찰 병합”은 동작을 발생 시키며 이는 “피처 브랜치”모델이 분해되기 시작 함을 의미합니다. 좋은 병합은 DVCS뿐만 아니라 모든 VCS의 기능이어야합니다.


에 따르면 거기에 지금의 --record-only문제점을 해결하는 데 사용할 수있는 스위치 --reintegrate문제를, 그리고 분명히 할 때 자동으로 재 통합을 수행하는 V1.8이 선택하는, 그리고 그것은 분기 이후 죽은 발생하지 않습니다


답변

Subversion 1.5 이전 (실수하지 않은 경우) 전에 Subversion은 병합 기록을 기억하지 못한다는 점에서 심각한 불만을 가지고있었습니다.

VonC의 사례를 살펴 보겠습니다.

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

개정판 A와 B를 주목하십시오. 어떤 이유로 든 개정 본 B의 개정판 A에서 “wss”분기에있는 개정판 A에서 “v2 전용”분기로 변경 사항을 병합했지만 두 분기를 계속 사용했다고 가정 해보십시오. 수은을 사용하여 두 분기를 다시 병합하려고하면 개정 A 및 B 이후의 변경 사항 만 병합합니다. Subversion을 사용하면 이전에 병합하지 않은 것처럼 모든 것을 병합해야합니다.

이것은 내 경험에 의한 예입니다. 코드의 양으로 인해 B에서 A로 병합하는 데 몇 시간이 걸렸습니다. 다시 시도하는 것은 정말 어려웠을 것입니다 . 이는 1.5 이전 버전의 경우와 같습니다.

Hginit 과의 병합 동작에서 더 관련성이 높은 또 다른 차이점 : Subversion Re-education :

당신과 내가 어떤 코드를 작업하고 있다고 생각하고, 그 코드를 분기하고, 우리는 각각 별도의 작업 공간으로 가서 그 코드를 개별적으로 많이 변경하여 아주 많이 분기했다고 상상해보십시오.

우리가 합병해야 할 때 Subversion은 수정 된 코드와 수정 된 코드 인 두 가지 수정본을 모두 보려고 시도하고 하나의 거룩하지 않은 엉망으로 수정하는 방법을 추측하려고합니다. 실제로는 충돌하지 않는 “병합 충돌”페이지와 페이지를 생성하는 데 실패합니다. Subversion이 우리가 한 일을 파악하지 못한 곳입니다.

반면 Mercurial에서 별도로 작업하는 동안 Mercurial은 일련의 변경 세트를 유지하는 데 바빴습니다. 따라서 코드를 통합하려는 경우 Mercurial은 실제로 훨씬 더 많은 정보를 보유하고 있습니다. 최종 제품을보고 코드를 작성하는 방법을 추측하기보다는 우리 각자가 변경 한 사항을 알고 해당 변경 사항을 다시 적용 할 수 있습니다. 함께.

요컨대, Mercurial의 차이점 분석 방식은 서브 버전보다 우수합니다.


답변