programing

Git에서의 병합이 SVN에서의 병합보다 어떻게 그리고/또는 왜 더 낫습니까?

yellowcard 2023. 6. 4. 10:26
반응형

Git에서의 병합이 SVN에서의 병합보다 어떻게 그리고/또는 왜 더 낫습니까?

분산 버전 제어 시스템이 빛을 발하는 주요 이유 중 하나는 SVN과 같은 기존 툴보다 훨씬 더 잘 통합되기 때문이라는 이야기를 몇 군데 들었습니다.이것은 실제로 두 시스템이 작동하는 방식의 본질적인 차이 때문입니까, 아니면 Git/Mercurial과 같은 특정 DVCS 구현이 SVN보다 더 영리한 병합 알고리즘을 가지고 있기 때문입니까?

하위 버전보다 DVCS에서 병합이 더 나은 이유에 대한 주장은 주로 얼마 전 하위 버전에서 분기 및 병합이 어떻게 작동했는지에 근거했습니다.1.5.0 이전 버전에서는 분기가 병합된 시기에 대한 정보를 저장하지 않았기 때문에 병합을 원할 때 병합해야 하는 수정사항의 범위를 지정해야 했습니다.

그럼 왜 서브버전 합병이 엉망이었을까요?

이 예를 곰곰이 생각해 보십시오.

      1   2   4     6     8
trunk o-->o-->o---->o---->o
       \
        \   3     5     7
b1       +->o---->o---->o

b1의 변경 사항을 트렁크에 병합하려면 트렁크가 체크아웃된 폴더에서 다음 명령을 실행합니다.

svn merge -r 2:7 {link to branch b1}

경사항병합니다에서 된 내용을 .b1로컬 작업 디렉토리로 이동합니다.그런 다음 충돌을 해결하고 결과를 테스트한 후 변경 내용을 커밋합니다.수정 트리를 커밋하면 다음과 같습니다.

      1   2   4     6     8   9
trunk o-->o-->o---->o---->o-->o      "the merge commit is at r9"
       \
        \   3     5     7
b1       +->o---->o---->o

그러나 버전 트리가 커지면 버전 트리가 커질 때 수정사항 범위를 지정하는 이 방법은 언제 어떤 수정사항이 병합되었는지에 대한 메타데이터가 없기 때문에 빠르게 통제할 수 없습니다.나중에 무슨 일이 일어나는지 곰곰이 생각해 보십시오.

           12        14
trunk  …-->o-------->o
                                     "Okay, so when did we merge last time?"
              13        15
b1     …----->o-------->o

이것은 주로 Subversion이 가지고 있는 저장소 설계의 문제입니다. 분기를 만들려면 저장소에 새 가상 디렉터리를 만들어야 합니다. 이 디렉터리에는 트렁크 복사본이 저장되지만 언제 어떤 항목이 다시 병합되었는지에 대한 정보는 저장되지 않습니다.이는 때때로 끔찍한 병합 충돌로 이어질 수 있습니다.더 나쁜 것은 Subversion이 기본적으로 양방향 병합을 사용했다는 것인데, 이는 두 개의 분기 헤드가 공통 조상과 비교되지 않을 때 자동 병합에 몇 가지 심각한 한계가 있습니다.

이 하위 버전을 완화하기 위해 이제 분기 및 병합을 위한 메타 데이터를 저장합니다.그러면 모든 문제가 해결될 거예요, 그렇죠?

아, 그나저나, 서브버전은 여전히 형편없군요...

전복과 같은 중앙 집중식 시스템에서는 가상 디렉터리가 최악입니다. 왜죠?왜냐하면 모든 사람들이 그것들을 볼 수 있기 때문입니다. 심지어 쓰레기 실험용 것들도요.분기는 실험을 하고 싶지만 모든 사람과 이모의 실험을 보고 싶지 않은 경우에 좋습니다.이것은 심각한 인지적 소음입니다.가지를 더 많이 추가할수록 더 많은 쓰레기를 볼 수 있습니다.

리포지토리에 공용 분기가 많을수록 모든 다른 분기를 추적하기가 어려워집니다.그래서 의문점은 브랜치가 아직 개발 중인지 아니면 중앙 집중식 버전 제어 시스템에서는 구분하기 어려운 정말로 죽은 것인지입니다.

대부분의 경우, 제가 본 바로는 조직은 기본적으로 하나의 큰 지점을 사용합니다.이는 결국 테스트 및 릴리스 버전을 추적하는 것이 어려울 것이기 때문에 유감스러운 일입니다. 다른 장점은 분기에서 비롯됩니다.

그렇다면 Git, Mercurial 및 Baza와 같은 DVCS가 분기 및 합병에서 Subversion보다 나은 이유는 무엇입니까?

분기가 최고의 개념이라는 매우 간단한 이유가 있습니다.가상 디렉터리는 설계상 존재하지 않으며 DVCS에서 분기는 단순히 저장소 동기화(예: 푸시 풀)로 작동하기 위해 필요한 하드 개체입니다.

DVCS로 작업할 때 가장 먼저 수행하는 작업은 리포지토리(git's, hg's 및 bzr's)를 복제하는 것입니다.복제는 개념적으로 버전 제어에서 분기를 생성하는 것과 동일합니다.어떤 사람들은 이것을 분기 또는 분기라고 부르지만( 후자는 공동으로 위치한 분기를 가리키는 데도 종종 사용되지만), 그것은 단지 같은 것입니다.모든 사용자는 자신의 리포지토리를 실행하므로 사용자별 분기가 진행됩니다.

버전 구조는 트리가 아니라 그래프입니다.더 구체적으로 방향성 비순환 그래프(DAG, 주기가 없는 그래프를 의미).각 커밋에 하나 이상의 상위 참조(커밋의 기반이 된 것)가 있는 경우를 제외하고는 DAG의 세부 사항을 자세히 설명할 필요가 없습니다.따라서 다음 그래프는 이 때문에 리비전 사이의 화살표를 역방향으로 보여줍니다.

입니다; 병의매간예다같라고 불리는 해 보세요. 라고 하는 중앙 저장소를 상상해 보십시오.origin앨리스라는 사용자가 자신의 컴퓨터에 저장소를 복제하고 있습니다.

         a…   b…   c…
origin   o<---o<---o
                   ^master
         |
         | clone
         v

         a…   b…   c…
alice    o<---o<---o
                   ^master
                   ^origin/master

복제 중에 발생하는 작업은 모든 수정본이 원래대로 Alice에 정확히 복사되고(유일하게 식별할 수 있는 해시 ID에 의해 검증됨) 오리진의 분기가 있는 위치를 표시하는 것입니다.

그런 다음 앨리스는 자신의 저장소에서 보고서를 작성하고 변경 사항을 적용하기로 결정합니다.

         a…   b…   c…
origin   o<---o<---o
                   ^ master

              "what'll happen after a push?"


         a…   b…   c…   d…   e…
alice    o<---o<---o<---o<---o
                             ^master
                   ^origin/master

은 매우, 해책은꽤다것니, 한은일유합이 하게 사용하는 입니다.origin저장소가 해야 할 일은 모든 새 리비전을 받아들여 최신 리비전("fast-forward")으로 분기를 이동하는 것입니다.

         a…   b…   c…   d…   e…
origin   o<---o<---o<---o<---o
                             ^ master

         a…   b…   c…   d…   e…
alice    o<---o<---o<---o<---o
                             ^master
                             ^origin/master

위에서 설명한 사용 사례는 어떤 것도 병합할 필요가 없습니다.따라서 3방향 병합 알고리즘은 모든 버전 제어 시스템에서 거의 동일하기 때문에 병합 알고리즘과 관련된 문제가 아닙니다.그 문제는 무엇보다 구조에 관한 것입니다.

그럼 실제 병합이 있는 예를 보여주시겠습니까?

분명히 위의 예는 매우 간단한 사용 사례이므로, 더 일반적인 예이긴 하지만 훨씬 더 꼬인 예를 들어 보겠습니다.하세요.origin세 번의 수정으로 시작했나요?음, 를 밥이라고 부르자, 그는 스스로 일했고, 그의 저장소에 대한 약속을 했습니다.

         a…   b…   c…   f…
bob      o<---o<---o<---o
                        ^ master
                   ^ origin/master

                   "can Bob push his changes?" 

         a…   b…   c…   d…   e…
origin   o<---o<---o<---o<---o
                             ^ master

이제 밥은 자신의 변경 사항을 직접적으로 적용할 수 없습니다.origin저소장은 Bob의이 Bob의 수정 사항에서 직접 입니다.origins, 이 경우에는 그렇지 않습니다.푸시를 시도할 경우 시스템에 "어..."와 유사한 메시지가 표시됩니다. 유감스럽게도 당신이 밥을 그렇게 하도록 놔둘 수 없습니다."

따라서 Bob은 (git's; org's 및; bzr's와 함께) 변경 사항을 풀인 후 병합해야 합니다.이것은 2단계 과정입니다.먼저 Bob은 새로운 개정판을 가져와야 합니다. 그러면 그것들은 그대로 복사될 것입니다.origin저장소 것을 할 수 .이제 그래프가 분기되는 것을 확인할 수 있습니다.

                        v master
         a…   b…   c…   f…
bob      o<---o<---o<---o
                   ^
                   |    d…   e…
                   +----o<---o
                             ^ origin/master

         a…   b…   c…   d…   e…
origin   o<---o<---o<---o<---o
                             ^ master

풀 프로세스의 두 번째 단계는 분기 팁을 병합하고 결과를 커밋하는 것입니다.

                                 v master
         a…   b…   c…   f…       1…
bob      o<---o<---o<---o<-------o
                   ^             |
                   |    d…   e…  |
                   +----o<---o<--+
                             ^ origin/master

병합에서 충돌이 발생하지 않기를 바랍니다. 충돌이 예상되는 경우 및 를 사용하여 두 단계를 수동으로 수행할 수 있습니다.나중에 해야 할 일은 이러한 변경 사항을 다시 적용하는 것입니다.origin병합 커밋이 최신 버전의 직계 후손이기 때문에 빠른 병합이 발생합니다.origin리포지토리:

                                 v origin/master
                                 v master
         a…   b…   c…   f…       1…
bob      o<---o<---o<---o<-------o
                   ^             |
                   |    d…   e…  |
                   +----o<---o<--+

                                 v master
         a…   b…   c…   f…       1…
origin   o<---o<---o<---o<-------o
                   ^             |
                   |    d…   e…  |
                   +----o<---o<--+

git와 hg를 병합하는 rebase라는 다른 옵션이 있는데, 이 옵션은 Bob의 변경사항을 최신 변경사항 이후로 이동합니다.저는 이 답변이 더 이상 장황하지 않기를 원하기 때문에 대신 그것에 대한 , 머큐리얼 또는 바자 문서를 읽을 수 있도록 하겠습니다.

독자를 위한 연습으로, 관련된 다른 사용자와 함께 어떻게 진행될지 그려보세요.위의 Bob 예와 유사하게 수행됩니다.모든 수정사항/커밋을 고유하게 식별할 수 있기 때문에 리포지토리 간 병합은 생각보다 쉽습니다.

각 개발자 간에 패치를 보내는 문제도 있는데, 서브버전에서는 고유하게 식별 가능한 수정사항으로 git, hg, bzr이 완화되는 큰 문제였습니다.다른 사용자가 변경사항을 병합한 후(즉, 병합 커밋) 팀의 다른 모든 사용자가 중앙 저장소로 푸시하거나 패치를 전송하여 사용하도록 전송하면 이미 변경사항이 발생했기 때문에 병합에 대해 걱정할 필요가 없습니다.마틴 파울러는 이런 방식을 비규칙적 통합이라고 부릅니다.

서브버전과 구조가 다르기 때문에 DAG를 사용하여 시스템뿐만 아니라 사용자도 보다 쉽게 분기 및 병합할 수 있습니다.

이전에는 서브버전이 병합 정보를 저장하지 않았기 때문에 양방향 병합만 수행할 수 있었습니다.여기에는 일련의 변경 사항을 적용하여 트리에 적용하는 작업이 포함됩니다.병합 정보를 사용하더라도 이는 여전히 가장 일반적으로 사용되는 병합 전략입니다.

Git는 기본적으로 3방향 병합 알고리즘을 사용하며, 이 알고리즘은 병합되는 헤드에 대한 공통 조상을 찾고 병합의 양쪽에 존재하는 지식을 활용하는 것을 포함합니다.이를 통해 Git은 충돌을 피하는 데 있어 더 지능적일 수 있습니다.

또한 Git에는 정교한 이름 바꾸기 코드가 있어 도움이 됩니다.변경 세트를 저장하거나 추적 정보를 저장하지 않습니다. 각 커밋 시 파일의 상태를 저장하고 휴리스틱을 사용하여 필요에 따라 이름 변경 및 코드 이동을 찾습니다(디스크 상의 스토리지는 이보다 더 복잡하지만 논리 계층에 표시되는 인터페이스는 추적하지 않습니다).

간단히 말해, 병합 구현은 SVN보다 Git에서 더 잘 수행됩니다.1.5 SVN 이전에는 병합 작업을 기록하지 않았기 때문에 SVN이 기록하지 않은 정보를 제공해야 하는 사용자의 도움 없이는 향후 병합을 수행할 수 없었습니다.1.5에서는 성능이 향상되었으며 실제로 SVN 스토리지 모델은 Git's DAG보다 약간 더 성능이 우수합니다.그러나 SVN은 Git보다 병합에 훨씬 더 많은 시간이 걸릴 수 있도록 병합 정보를 다소 복잡한 형태로 저장했습니다. 저는 실행 시간에서 300개의 요인을 관찰했습니다.

또한 SVN은 이동된 파일의 병합을 돕기 위해 이름 변경을 추적한다고 주장합니다.그러나 실제로는 복사본과 별도의 삭제 작업으로 저장되며, 병합 알고리즘은 수정/이름 변경 상황, 즉 파일이 한 분기에서 수정되고 다른 분기에서 이름이 변경되면 해당 분기에서 병합됩니다.이러한 상황에서는 여전히 가상 병합 충돌이 발생하고 디렉터리 이름 변경의 경우 수정 내용이 자동으로 손실됩니다.(그러면 SVN 사람들은 수정 사항이 여전히 역사에 남아 있다고 지적하는 경향이 있지만, 수정 사항이 나타나야 할 병합 결과에 있지 않을 때는 별로 도움이 되지 않습니다.

반면, Git은 이름을 추적하지도 않고 사실 이후(합병 시) 그것들을 파악하고 마법처럼 그렇게 합니다.

. 할 수 (SVN 병합있다습니문가제. 1.5/1).6에서는 원하는 만큼 자주 트렁크에서 분기로 병합할 수 있지만 다른 방향으로의 병합은 공지해야 합니다.--reintegrate할 수 .), 를 참조하십시오.사실이 을 알게 a) 씬후에그이사실로아실제이니것그는알되을었게고라것훨이들은, a)그▁a▁much▁a▁out▁found▁this▁that)▁isn▁that,,되었▁the.--reintegrate 자동으로 파악할 수 있으며, b) 양방향으로 반복 병합이 가능합니다.

그러나 이 모든 것(IMHO는 그들이 무엇을 하고 있는지에 대한 이해가 부족함을 보여주고 있음) 이후에 저는 (좋아요, 저는) 사소한 분기 시나리오에서 SVN을 사용하는 것에 매우 주의할 것이고 이상적으로 Git이 병합 결과에 대해 어떻게 생각하는지 보려고 노력할 것입니다.

SVN의 지점에 대한 강제적인 글로벌 가시성과 같은 답변에서 제시된 다른 요점은 병합 기능과 관련이 없습니다(단, 유용성과 관련이 있음).또한, 'Git store는 변하는 반면 SVN store는 다른 것'은 대부분 요점에서 벗어났습니다.Git는 개념적으로 각 커밋을 별도의 트리(tar 파일과 같은)로 저장한 다음 이를 효율적으로 저장하기 위해 상당한 휴리스틱을 사용합니다.두 커밋 간의 변경 사항을 계산하는 것은 스토리지 구현과는 별개입니다.Git는 SVN이 병합 정보를 수행하는 훨씬 간단한 형태로 DAG 기록을 저장합니다.후자를 이해하려는 사람은 누구나 제 말이 무슨 뜻인지 알 것입니다.

요약하자면, Git은 SVN보다 훨씬 간단한 데이터 모델을 사용하여 수정사항을 저장하므로, 표현 => 보다 실질적으로 더 나은 병합을 처리하기보다는 실제 병합 알고리즘에 많은 에너지를 투입할 수 있습니다.

다른 답변에서 언급되지 않은 한 가지는 DVCS의 큰 장점으로, 변경 사항을 적용하기 전에 로컬에서 커밋할 수 있다는 것입니다. 제가 그했을 때, 이것은 , 제체하싶변있사고, 이서같누가지미가서군, ▁an▁in▁sv▁ton▁i▁in▁that▁sv▁i▁do▁when▁this▁had한의것미했수▁i습는작다니을n다,▁in행제해야을업가이▁a▁and은svn update내가 저지르기도 전에이는 내 변경사항과 다른 사용자의 변경사항이 함께 혼합되어 병합을 중단할 방법이 없음을 의미합니다.git reset또는hg update -C), 때문입니다라는 이름으로 시작합니다.병합이 중요하지 않은 경우 병합 결과를 정리하기 전에 피쳐에 대한 작업을 계속할 수 없음을 의미합니다.

하지만 너무 멍청해서 별도의 지점을 사용할 수 없는 사람들에게는 이점일 수도 있습니다(제 기억이 맞다면, 제가 SVN을 사용했던 회사에서는 개발에 사용된 지점이 하나밖에 없었습니다).

편집: 주로 질문의 이 부분을 다룹니다.
이것은 실제로 두 시스템이 작동하는 방식의 본질적인 차이 때문입니까, 아니면 Git/Mercurial과 같은 특정 DVCS 구현이 SVN보다 더 영리한 병합 알고리즘을 가지고 있기 때문입니까?
TL;DR - 이러한 특정 도구는 더 나은 알고리즘을 제공합니다.분산되는 것은 워크플로우 이점이 있지만 병합 이점과 직교합니다.
종료

저는 합격한 답변을 읽었습니다.그것은 명백한 잘못입니다.

SVN 병합은 번거롭고 번거로울 수 있습니다.하지만, 그것이 실제로 어떻게 작동하는지 잠시 무시하세요.Git가 보유하거나 SVN이 보유하지 않거나 도출할 수 있는 정보는 없습니다.더 중요한 것은 버전 제어 시스템의 별도 복사본(때로는 부분적인)을 유지하는 것이 더 많은 실제 정보를 제공할 이유가 없다는 것입니다.두 구조는 완전히 동일합니다.

당신이 Git이 "더 잘하는" "똑똑한 것"을 하고 싶다고 가정합니다.그리고 당신의 물건은 SVN에 체크인됩니다.

SVN을 동등한 Git 양식으로 변환하고 Git에서 수행한 다음 여러 커밋을 사용하여 일부 추가 분기에서 결과를 확인합니다.SVN 문제를 Git 문제로 전환하는 자동화된 방법을 상상할 수 있다면 Git에는 근본적인 이점이 없습니다.

결국, 어떤 버전의 제어 시스템이든 제가 할 수 있습니다.

1. Generate a set of objects at a given branch/revision.
2. Provide the difference between a parent child branch/revisions.

또한 병합의 경우에는 다음 사항을 아는 것이 유용합니다(또는 중요).

3. The set of changes have been merged into a given branch/revision.

Mercurial, Git 및 Subversion(현재는 기본적으로 이전에는 svnmerge를 사용함).py) 세 가지 정보를 모두 제공할 수 있습니다.DVC를 통해 근본적으로 더 나은 것을 입증하려면 Git/Mercurial/DVC에서 사용할 수 있는 네 번째 정보를 몇 가지 지적하십시오. SVN/중앙 집중식 VC에서는 사용할 수 없습니다.

그렇다고 그것들이 더 나은 도구가 아니라는 뜻은 아닙니다!

SVN은 파일을 추적하고 Git은 추적합니다. 내용물 변화한 클래스/파일에서 다른 클래스로 리팩터링된 코드 블록을 추적할 수 있을 정도로 영리합니다.소스를 추적하기 위해 두 가지 완전히 다른 접근 방식을 사용합니다.

저는 여전히 SVN을 많이 사용하지만, Git을 사용한 적이 거의 없어 매우 기쁩니다.

시간이 있다면 좋은 읽을거리:Git를 선택한 이유

조엘의 블로그(슬프게도 그의 마지막 블로그)에 있는 기사를 읽으십시오.이것은 Mercurial에 관한 것이지만 Git와 같은 분산 VC 시스템의 장점에 대해 이야기합니다.

분산 버전 제어를 사용하는 경우 분산 부분은 실제로 가장 흥미로운 부분이 아닙니다.흥미로운 부분은 이러한 시스템이 버전이 아닌 변화의 관점에서 생각한다는 것입니다.

여기에서 기사를 읽으십시오.

언급URL : https://stackoverflow.com/questions/2471606/how-and-or-why-is-merging-in-git-better-than-in-svn

반응형