Programming

병합 : Hg / Git vs. SVN

procodes 2020. 6. 18. 22:30
반응형

병합 : Hg / Git vs. SVN


나는 종종 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.

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

  • 당신은 새로운 제공해야합니다 -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-checkout
svnadmin은 svn-repo를 만듭니다
svn 체크 아웃 파일 : /// tmp / svn-repo svn-checkout
cd svn- 체크 아웃
mkdir 트렁크 브랜치
에코 '안녕, 세상!' > trunk / hello.txt
svn 트렁크 분기 추가
svn commit -m '초기 가져 오기.'
svn copy '^ / trunk' '^ / branches / rename'-m '지점 작성'
svn 스위치 '^ / trunk'.
에코 '안녕하세요, 세계!' > hello.txt
svn commit -m '트렁크에서 업데이트.'
svn 스위치 '^ / branches / rename'.
svn 이름 바꾸기 hello.txt hello.en.txt
svn commit -m '브랜치 이름 바꾸기'
svn 스위치 '^ / trunk'.
svn merge- '^ / 분기 / 이름 바꾸기'

이 책에 따르면, 병합은 깨끗하게 완료되지만 업데이트 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)

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

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

The other feature I like (which influence merges) is the ability to squash commits (in a branch not yet pushed to another repo) in order to present:

  • a cleaner history
  • commits which are more coherent (instead of commit1 for function1, commit2 for function2, commit3 again for function1...)

That ensure merges which are a lot easier, with less conflicts.


We recently migrated from SVN to GIT, and faced this same uncertainty. There was a lot of anecdotal evidence that GIT was better, but it was hard to come across any examples.

I can tell you though, that GIT is A LOT BETTER at merging than SVN. This is obviously anecdotal, but there is a table to follow.

Here are some of the things we found:

  • SVN used to throw up a lot of tree conflicts in situations where it seemed like it shouldn't. We never got to the bottom of this but it doesn't happen in GIT.
  • While better, GIT is significantly more complicated. Spend some time on training.
  • We were used to Tortoise SVN, which we liked. Tortoise GIT is not as good and this may put you off. However I now use the GIT command line which I much prefer to Tortoise SVN or any of the GIT GUI's.

When we were evaluating GIT we ran the following tests. These show GIT as the winner when it comes to merging, but not by that much. In practice the difference is much larger, but I guess we haven't managed to replicate the situations that SVN handles badly.

GIT vs SVN Merging Evaluation


Others have covered the more theoretical aspects of this. Maybe I can lend a more practical perspective.

I'm currently working for a company that uses SVN in a "feature branch" development model. That is:

  • No work can be done on trunk
  • Each developer can have create their own branches
  • Branches should last for the duration of the task undertaken
  • Each task should have it's own branch
  • Merges back to trunk need to be authorized (normally via bugzilla)
  • At times when high levels of control are needed, merges may be done by a gatekeeper

In general, it works. SVN can be used for a flow like this, but it's not perfect. There are some aspects of SVN which get in the way and shape human behaviour. That gives it some negative aspects.

  • We've had quite a few problems with people branching from points lower than ^/trunk. This litters merge information records throughout the tree, and eventually breaks the merge tracking. False conflicts start appearing, and confusion reigns.
  • Picking up changes from trunk into a branch is relatively straight forward. svn merge does what you want. Merging your changes back requires (we're told) --reintegrate on the merge command. I've never truly understood this switch, but it means that the branch can't be merged into trunk again. This means it's a dead branch and you have to create a new one to continue work. (See note)
  • The whole business of doing operations on the server via URLs when creating and deleting branches really confuses and scares people. So they avoid it.
  • Switching between branches is easy to get wrong, leaving part of a tree looking at branch A, whilst leaving another part looking at branch B. So people prefer to do all their work in one branch.

What tends to happen is that an engineer creates a branch on day 1. He starts his work and forgets about it. Some time later a boss comes along and asks if he can release his work to trunk. The engineer has been dreading this day because reintegrating means:

  • Merging his long lived branch back into trunk and solving all conflicts, and releasing unrelated code that should have been in a separate branch, but wasn't.
  • Deleting his branch
  • Creating a new branch
  • Switching his working copy to the new branch

...and because the engineer does this as little as they can, they can't remember the "magic incantation" to do each step. Wrong switches and URLs happen, and suddenly they're in a mess and they go get the "expert".

Eventually it all settles down, and people learn how to deal with the shortcomings, but each new starter goes through the same problems. The eventual reality (as opposed to what I set out at he start) is:

  • No work is done on trunk
  • Each developer has one major branch
  • Branches last until work needs to be released
  • Ticketed bug fixes tend to get their own branch
  • Merges back to trunk are done when authorized

...but...

  • Sometimes work makes it to trunk when it shouldn't because it's in the same branch as something else.
  • People avoid all merging (even easy stuff), so people often work in their own little bubbles
  • Big merges tend to occur, and cause a limited amount of chaos.

Thankfully the team is small enough to cope, but it wouldn't scale. Thing is, none of this is a problem with CVCS, but more that because merges aren't as important as in DVCS they're not as slick. That "merge friction" causes behaviour which means that a "Feature Branch" model starts to break down. Good merges need to be a feature of all VCS, not just DVCS.


According to this there's now a --record-only switch that could be used to solve the --reintegrate problem, and apparently v1.8 chooses when to do a reintegrate automatically, and it doesn't cause the branch to be dead afterwards


Prior to subversion 1.5 (if I'm not mistaken), subversion had a significant dissadvantage in that it would not remember merge history.

Let's look at the case outlined by VonC:

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

Notice revisions A and B. Say you merged changes from revision A on the "wss" branch to the "v2-only" branch at revision B (for whatever reason), but continued using both branches. If you tried to merge the two branches again using mercurial, it would only merge changes after revisions A and B. With subversion, you'd have to merge everything, as if you didn't do a merge before.

This is an example from my own experience, where merging from B to A took several hours due to the volume of code: that would have been a real pain to go through again, which would have been the case with subversion pre-1.5.

Another, probably more relevant difference in merge behaviour from Hginit: Subversion Re-education:

Imagine that you and I are working on some code, and we branch that code, and we each go off into our separate workspaces and make lots and lots of changes to that code separately, so they have diverged quite a bit.

When we have to merge, Subversion tries to look at both revisions—my modified code, and your modified code—and it tries to guess how to smash them together in one big unholy mess. It usually fails, producing pages and pages of “merge conflicts” that aren’t really conflicts, simply places where Subversion failed to figure out what we did.

By contrast, while we were working separately in Mercurial, Mercurial was busy keeping a series of changesets. And so, when we want to merge our code together, Mercurial actually has a whole lot more information: it knows what each of us changed and can reapply those changes, rather than just looking at the final product and trying to guess how to put it together.

In short, Mercurial's way of analyzing differences is (was?) superior to subversion's.

참고URL : https://stackoverflow.com/questions/2475831/merging-hg-git-vs-svn

반응형