브랜치가 별도의 개발 라인을 유지하는 데 사용되는 경우, 특정 단계에서 한 브랜치에서 변경된 내용을 트렁크로 다시 병합하거나 그 반대로 하고 싶을 것입니다.
Subversion에서 브랜치와 병합이 어떻게 작동하는지 이해하는 것이 상당히 복잡할 수 있으므로 사용하기 전에 중요합니다. Subversion 책의 브랜치 및 병합 장을 읽어볼 것을 강력히 권장합니다. 이 장에서는 사용 방법에 대한 전체 설명과 많은 예제를 제공합니다.
다음으로 주의할 점은 병합은 항상 작업 사본(working copy) 내에서 이루어진다는 것입니다. 변경 사항을 브랜치로 병합하려면 해당 브랜치의 작업 사본을 체크아웃해야 하며, 해당 작업 사본에서 → 을 사용하여 병합 마법사를 호출해야 합니다.
일반적으로 수정되지 않은 작업 사본으로 병합을 수행하는 것이 좋습니다. WC(작업 사본)에서 다른 변경 사항을 만들었다면 먼저 커밋하십시오. 병합이 예상대로 진행되지 않으면 변경 사항을 되돌리고 싶을 수 있으며, 되돌리기(Revert) 명령은 병합 전에 수행한 모든 변경 사항을 포함하여 모든 변경 사항을 버립니다.
아래에 설명된 대로 약간 다른 방식으로 처리되는 세 가지 일반적인 병합 사용 사례가 있습니다. 병합 마법사의 첫 페이지에서 필요한 방법을 선택하도록 요청합니다.
이 방법은 브랜치(또는 트렁크)에 하나 이상의 리비전을 만들고 해당 변경 사항을 다른 브랜치로 이식하려는 경우를 다룹니다.
Subversion에 요청하는 내용은 다음과 같습니다: “ 브랜치 A의 리비전 1 [에서] 브랜치 A의 리비전 7 [까지] 얻는 데 필요한 변경 사항을 계산하고, 해당 변경 사항을 내 작업 사본(트렁크 또는 브랜치 B)에 적용하십시오. ”
리비전 범위를 비워두면 Subversion은 병합 추적 기능을 사용하여 사용할 올바른 리비전 범위를 계산합니다. 이는 재통합(reintegrate) 또는 자동 병합으로 알려져 있습니다.
이것은 재통합 방법의 보다 일반적인 경우입니다. Subversion에 요청하는 내용은 다음과 같습니다: “ 트렁크의 HEAD 리비전 [에서] 브랜치의 HEAD 리비전 [까지] 얻는 데 필요한 변경 사항을 계산하고, 해당 변경 사항을 내 작업 사본(트렁크의)에 적용하십시오. ” 그 결과 트렁크는 이제 브랜치와 똑같아 보입니다.
서버/저장소가 병합 추적을 지원하지 않는 경우, 이것이 브랜치를 트렁크로 다시 병합하는 유일한 방법입니다. 또 다른 사용 사례는 벤더 브랜치를 사용하고 새 벤더 드롭 후 변경 사항을 트렁크 코드로 병합해야 할 때 발생합니다. 더 자세한 정보는 Subversion 책의 벤더 브랜치 장을 참조하십시오.
원본(From): 필드에 작업 사본으로 이식하려는 변경 사항이 포함된 브랜치 또는 태그의 전체 폴더 URL을 입력하십시오. 을 클릭하여 저장소를 탐색하고 원하는 브랜치를 찾을 수도 있습니다. 이 브랜치에서 이전에 병합한 적이 있다면, 이전에 사용했던 URL 기록을 보여주는 드롭다운 목록을 사용하십시오.
이름이 바뀌거나 삭제된 브랜치에서 병합하는 경우, 해당 브랜치가 여전히 존재했던 리비전으로 돌아가야 합니다. 이 경우, 병합되는 리비전 범위에서 해당 리비전을 고정 리비전(peg revision)으로 지정해야 합니다(아래 참조). 그렇지 않으면 HEAD에서 해당 경로를 찾을 수 없을 때 병합이 실패합니다.
병합할 리비전 범위 필드에 병합하려는 리비전 목록을 입력하십시오. 이는 단일 리비전, 쉼표로 구분된 특정 리비전 목록, 대시로 구분된 리비전 범위 또는 이들의 조합일 수 있습니다.
병합에 대한 고정 리비전을 지정해야 하는 경우, 리비전 끝에 고정 리비전을 추가하십시오(예: 5-7,10@3). 위 예시에서는 리비전 5, 6, 7, 10이 병합되고, 3이 고정 리비전이 됩니다.
TortoiseSVN과 명령줄 클라이언트에서 리비전 범위를 지정하는 방식에는 중요한 차이가 있습니다. 이를 가장 쉽게 시각화하는 방법은 기둥과 울타리 패널이 있는 울타리를 생각하는 것입니다.
명령줄 클라이언트에서는 이전 및 이후 지점을 지정하는 두 개의 “울타리 기둥” 리비전을 사용하여 병합할 변경 사항을 지정합니다.
TortoiseSVN에서는 “울타리 패널”을 사용하여 병합할 변경 집합(changeset)을 지정합니다. 이는 각 리비전이 변경 집합으로 나타나는 로그 대화 상자를 사용하여 병합할 리비전을 지정할 때 명확해집니다.
리비전을 청크 단위로 병합하는 경우, Subversion 책에 제시된 방법은 이번에는 100-200을 병합하고 다음번에는 200-300을 병합하게 합니다. TortoiseSVN에서는 이번에는 100-200을 병합하고 다음번에는 201-300을 병합합니다.
이러한 차이로 인해 메일링 리스트에서 많은 논쟁이 있었습니다. 명령줄 클라이언트와의 차이가 있음을 인정하지만, 대다수의 GUI 사용자에게는 우리가 구현한 방법이 이해하기 더 쉽다고 생각합니다.
필요한 리비전 범위를 선택하는 가장 쉬운 방법은 를 클릭하는 것입니다. 이렇게 하면 최근 변경 사항과 로그 댓글이 나열됩니다. 단일 리비전의 변경 사항을 병합하려면 해당 리비전을 선택하기만 하면 됩니다. 여러 리비전의 변경 사항을 병합하려면 해당 범위를 선택하십시오(일반적인 Shift 키를 사용하여). 을 클릭하면 병합할 리비전 번호 목록이 자동으로 채워집니다.
작업 사본에서 변경 사항을 다시 제외하거나, 이미 커밋된 변경 사항을 되돌리려면, 되돌릴 리비전을 선택하고 역방향 병합(Reverse merge) 상자가 선택되어 있는지 확인하십시오.
이 브랜치에서 일부 변경 사항을 이미 병합했다면, 변경 사항을 커밋할 때 로그 메시지에 마지막으로 병합된 리비전을 기록해 두셨기를 바랍니다. 이 경우 작업 사본에 대해 를 사용하여 해당 로그 메시지를 추적할 수 있습니다. 리비전을 변경 집합으로 생각한다는 점을 기억하면, 이전 병합의 종료 지점 다음 리비전을 이번 병합의 시작점으로 사용해야 합니다. 예를 들어, 지난번에 리비전 37부터 39까지 병합했다면, 이번 병합의 시작점은 리비전 40이어야 합니다.
Subversion의 병합 추적 기능을 사용 중이라면, 이미 병합된 리비전을 기억할 필요가 없습니다. Subversion이 이를 기록해 줍니다. 리비전 범위를 비워두면 아직 병합되지 않은 모든 리비전이 포함됩니다. 더 자세한 내용은 “병합 추적” 섹션을 참조하십시오.
병합 추적을 사용할 때, 로그 대화 상자에는 이전에 병합된 리비전과 공통 조상 시점 이전, 즉 브랜치가 복사되기 이전의 리비전이 회색으로 표시됩니다. 병합할 수 없는 리비전 숨기기(Hide non-mergeable revisions) 체크박스를 사용하면 이러한 리비전을 완전히 필터링하여 병합 가능한 리비전만 볼 수 있습니다.
다른 사람들이 변경 사항을 커밋할 수 있는 경우, HEAD 리비전 사용에 주의하십시오. 다른 사람이 마지막 업데이트 이후에 커밋을 했다면, 여러분이 생각하는 리비전을 가리키지 않을 수 있습니다.
리비전 범위를 비워두거나 모든 리비전(all revisions) 라디오 버튼이 선택되어 있으면, Subversion은 아직 병합되지 않은 모든 리비전을 병합합니다. 이는 재통합 또는 자동 병합으로 알려져 있습니다.
재통합 병합에 적용되는 몇 가지 조건이 있습니다. 첫째, 서버는 병합 추적을 지원해야 합니다. 작업 사본은 깊이가 무한해야 하며(스파스 체크아웃 없음), 로컬 수정 사항, 전환된 항목 또는 HEAD 이외의 리비전으로 업데이트된 항목이 없어야 합니다. 브랜치 개발 중에 트렁크에 대한 모든 변경 사항은 브랜치로 병합되었거나(또는 병합된 것으로 표시되어야 합니다). 병합할 리비전 범위는 자동으로 계산됩니다.
을 클릭하여 “병합 옵션” 섹션으로 이동하십시오.
이 방법을 사용하여 기능 브랜치를 트렁크로 다시 병합하는 경우, 트렁크의 작업 사본 내에서 병합 마법사를 시작해야 합니다.
원본(From): 필드에 트렁크의 전체 폴더 URL을 입력하십시오. 이것이 잘못된 것처럼 들릴 수 있지만, 트렁크가 브랜치 변경 사항을 추가하려는 시작점이라는 것을 기억하십시오. 을 클릭하여 저장소를 탐색할 수도 있습니다.
대상(To): 필드에 기능 브랜치의 전체 폴더 URL을 입력하십시오.
원본 리비전(From Revision) 필드와 대상 리비전(To Revision) 필드 모두에 두 트리가 동기화된 마지막 리비전 번호를 입력하십시오. 다른 사람이 커밋하지 않는다고 확신한다면 두 경우 모두 HEAD 리비전을 사용할 수 있습니다. 해당 동기화 이후 다른 사람이 커밋했을 가능성이 있다면, 더 최근 커밋을 잃지 않도록 특정 리비전 번호를 사용하십시오.
를 사용하여 리비전을 선택할 수도 있습니다.
이 마법사 페이지에서는 병합 프로세스를 시작하기 전에 고급 옵션을 지정할 수 있습니다. 대부분의 경우 기본 설정을 사용하면 됩니다.
병합에 사용할 깊이, 즉 병합이 작업 사본의 얼마나 깊이까지 진행되어야 하는지 지정할 수 있습니다. 사용되는 깊이 용어는 “체크아웃 깊이” 섹션에 설명되어 있습니다. 기본 깊이는 작업 사본(Working copy)이며, 이는 기존 깊이 설정을 사용하며 거의 항상 여러분이 원하는 것입니다.
대부분의 경우 파일의 히스토리를 고려하여 병합하기를 원하므로, 공통 조상에 상대적인 변경 사항이 병합됩니다. 때로는 관련이 있을 수 있지만 저장소에는 없는 파일을 병합해야 할 수도 있습니다. 예를 들어, 타사 라이브러리의 버전 1과 2를 두 개의 별도 디렉터리로 가져왔을 수 있습니다. 논리적으로 관련되어 있더라도, Subversion은 가져온 tarball만 인식하므로 이를 알지 못합니다. 이 두 트리 간의 차이를 병합하려고 하면 완전한 제거와 완전한 추가가 표시될 것입니다. Subversion이 히스토리 기반 차이 대신 경로 기반 차이만 사용하도록 하려면 조상 무시(Ignore ancestry) 상자를 선택하십시오. 이 주제에 대해 더 자세히 알아보려면 Subversion 책의 조상 인식 또는 무시 를 참조하십시오.
줄 끝 및 공백 변경 사항이 처리되는 방식을 지정할 수 있습니다. 이러한 옵션은 “줄 끝 및 공백 옵션” 섹션에 설명되어 있습니다. 기본 동작은 모든 공백 및 줄 끝 차이를 병합되어야 할 실제 변경 사항으로 취급하는 것입니다.
강제 병합(Force the merge)으로 표시된 체크박스는 들어오는 삭제가 로컬에서 수정되었거나 전혀 버전 관리되지 않은 파일에 영향을 미칠 때 트리 충돌을 방지하는 데 사용됩니다. 파일이 삭제되면 복구할 방법이 없으므로 기본적으로 이 옵션은 선택되어 있지 않습니다.
혼합 리비전 허용(권장하지 않음)(Allow mixed revisions (not recommended))으로 표시된 체크박스는 혼합 리비전 작업 사본으로의 병합을 허용하는 데 사용됩니다(--allow-mixed-revisions 명령줄 옵션에 해당). Subversion 1.7부터 기본적으로 혼합 리비전 작업 사본으로의 병합을 허용하지 않습니다. 이유는 Subversion 책의 브랜치 동기화 유지 섹션 끝 부분에 설명되어 있습니다. 혼합 리비전 작업 사본으로 병합할 때 발생할 수 있는 문제를 이해한다면, 이 체크박스를 선택하여 병합을 수행할 수 있습니다.
병합 추적을 사용하고 있고, 실제로 여기서 병합을 수행하지 않고 리비전을 병합된 것으로 표시하려면 병합만 기록(Only record the merge) 체크박스를 선택하십시오. 이렇게 하려는 두 가지 가능한 이유가 있습니다. 병합 알고리즘에 비해 병합이 너무 복잡해서 변경 사항을 수동으로 코딩한 다음, 병합 추적 알고리즘이 이를 인식하도록 변경 사항을 병합된 것으로 표시할 수 있습니다. 또는 특정 리비전이 병합되는 것을 방지하고 싶을 수도 있습니다. 이미 병합된 것으로 표시하면 병합 추적을 인식하는 클라이언트에서 병합이 발생하지 않습니다.
이제 모든 설정이 완료되었습니다. 버튼을 클릭하기만 하면 됩니다. 결과를 미리 보려면 이 병합 작업을 시뮬레이션하지만, 작업 사본을 전혀 수정하지 않습니다. 실제 병합으로 변경될 파일 목록을 보여주고, 충돌이 발생할 수 있는 파일을 기록합니다. 병합 추적은 병합 프로세스를 훨씬 더 복잡하게 만들기 때문에, 병합이 충돌 없이 완료될지 미리 알아낼 수 있는 보장된 방법은 없습니다. 따라서 테스트 병합에서 충돌이 발생한 것으로 표시된 파일도 실제로는 문제 없이 병합될 수 있습니다.
병합 진행 대화 상자는 관련된 리비전 범위와 함께 병합의 각 단계를 보여줍니다. 이는 예상했던 것보다 하나 더 많은 리비전을 나타낼 수 있습니다. 예를 들어, 리비전 123을 병합하도록 요청했다면 진행 대화 상자에는 “리비전 122부터 123까지 병합 중”이라고 보고될 것입니다. 이를 이해하려면 병합이 Diff와 밀접하게 관련되어 있다는 점을 기억해야 합니다. 병합 프로세스는 저장소의 두 지점 간의 차이 목록을 생성하고, 해당 차이를 작업 사본에 적용하는 방식으로 작동합니다. 진행 대화 상자는 단순히 Diff의 시작점과 끝점을 보여주는 것입니다.
병합이 완료되었습니다. 병합 결과를 확인하고 예상대로인지 살펴보는 것이 좋습니다. 병합은 일반적으로 상당히 복잡합니다. 브랜치가 트렁크에서 너무 멀리 벗어났다면 충돌이 자주 발생합니다.
리비전이 작업 사본으로 병합될 때마다 TortoiseSVN은 병합된 모든 리비전에서 로그 메시지를 생성합니다. 이 메시지는 커밋 대화 상자의 버튼에서 사용할 수 있습니다.
생성된 메시지를 사용자 정의하려면 작업 사본에 해당 프로젝트 속성을 설정하십시오. “병합 로그 메시지 템플릿” 섹션을 참조하십시오.
Subversion 클라이언트 및 서버 1.5 이전 버전에서는 병합 정보가 저장되지 않으며 병합된 리비전을 수동으로 추적해야 합니다. 변경 사항을 테스트하고 이 리비전을 커밋할 때, 커밋 로그 메시지에는 병합에서 이식된 리비전 번호가 항상 포함되어야 합니다. 나중에 다른 병합을 적용하려면 이미 무엇을 병합했는지 알아야 합니다. 변경 사항을 두 번 이상 이식하고 싶지 않기 때문입니다. 이에 대한 자세한 내용은 Subversion 책의 병합을 위한 모범 사례 를 참조하십시오.
서버와 모든 클라이언트가 Subversion 1.5 이상을 실행하고 있다면, 병합 추적 기능이 병합된 리비전을 기록하고 리비전이 두 번 이상 병합되는 것을 방지합니다. 이렇게 하면 매번 전체 리비전 범위를 병합하기만 하면 되고, 실제로는 새 리비전만 병합된다는 것을 알 수 있으므로 작업이 훨씬 간단해집니다.
브랜치 관리는 중요합니다. 이 브랜치를 트렁크와 최신 상태로 유지하려면 브랜치와 트렁크가 너무 멀리 떨어지지 않도록 자주 병합해야 합니다. 물론, 위에서 설명한 바와 같이 변경 사항의 반복적인 병합은 여전히 피해야 합니다.
기능 브랜치를 트렁크로 다시 병합했다면, 이제 트렁크는 모든 새 기능 코드를 포함하며 브랜치는 더 이상 사용되지 않습니다. 필요한 경우 저장소에서 삭제할 수 있습니다.
Subversion은 파일과 폴더를 서로 병합할 수 없으며, 폴더는 폴더에만, 파일은 파일에만 병합할 수 있습니다. 파일을 클릭하고 병합 대화 상자를 열면 해당 대화 상자에서 파일 경로를 지정해야 합니다. 폴더를 선택하고 대화 상자를 열면 병합을 위해 폴더 URL을 지정해야 합니다.
Subversion 1.5는 병합 추적 기능을 도입했습니다. 한 트리에서 다른 트리로 변경 사항을 병합하면 병합된 리비전 번호가 저장되며, 이 정보는 여러 다른 목적으로 사용될 수 있습니다.
동일한 리비전을 두 번 병합하는 위험(반복 병합 문제)을 피할 수 있습니다. 한 번 병합된 것으로 표시된 리비전은 해당 리비전을 범위에 포함하는 향후 병합에서 건너뛰게 됩니다.
브랜치를 트렁크로 다시 병합할 때, 로그 대화 상자는 트렁크 로그의 일부로 브랜치 커밋을 보여줄 수 있어 변경 사항의 추적성을 향상시킵니다.
병합 대화 상자 내에서 로그 대화 상자를 표시할 때, 이미 병합된 리비전은 회색으로 표시됩니다.
파일에 대한 blame 정보를 표시할 때, 병합을 수행한 사람 대신 병합된 리비전의 원래 작성자를 표시하도록 선택할 수 있습니다.
실제로 병합을 수행하지 않고 병합된 리비전 목록에 포함함으로써 리비전을 병합하지 않음으로 표시할 수 있습니다.
병합 추적 정보는 클라이언트가 병합을 수행할 때 svn:mergeinfo 속성에 저장됩니다. 병합이 커밋되면 서버는 해당 정보를 데이터베이스에 저장하고, 병합, 로그 또는 blame 정보를 요청할 때 서버는 적절하게 응답할 수 있습니다. 시스템이 제대로 작동하려면 서버, 저장소 및 모든 클라이언트가 업그레이드되었는지 확인해야 합니다. 이전 클라이언트는 svn:mergeinfo 속성을 저장하지 않으며, 이전 서버는 새 클라이언트가 요청한 정보를 제공하지 않습니다.
Subversion의 병합 추적 문서 에서 병합 추적에 대해 더 자세히 알아보십시오.
충돌 해결 대화 상자의 텍스트는 SVN 라이브러리에서 제공되므로 TortoiseSVN 대화 상자와 같이 (아직) 번역되지 않을 수 있습니다. 죄송합니다.
병합이 항상 원활하게 진행되는 것은 아닙니다. 때로는 충돌이 발생하기도 합니다. TortoiseSVN은 병합 충돌 대화 상자를 표시하여 이 프로세스를 돕습니다.
일부 변경 사항은 원활하게 병합되었을 가능성이 높지만, 다른 로컬 변경 사항은 이미 저장소에 커밋된 변경 사항과 충돌할 수 있습니다. 병합 가능한 모든 변경 사항은 병합됩니다. 병합 충돌 대화 상자는 충돌하는 줄을 처리하는 다양한 방법을 제공합니다.
파일 내용 또는 속성 변경으로 인해 발생하는 일반적인 충돌의 경우, 대화 상자에는 충돌하는 부분 중 어떤 것을 유지하거나 거부할지 선택할 수 있는 버튼이 표시됩니다.
지금은 충돌을 처리하지 마십시오. 병합을 계속 진행하고 병합이 완료된 후 충돌을 해결하십시오.
이것은 병합에서 오는 변경 사항이나 작업 사본에서 만든 변경 사항 없이 파일을 원래 상태로 유지합니다.
이것은 모든 로컬 변경 사항을 버리고 병합 원본에서 오는 파일을 사용합니다.
이것은 병합 원본의 모든 변경 사항을 버리고 파일을 로컬 편집 상태로 유지합니다.
이것은 병합 원본의 변경 사항과 충돌하는 로컬 변경 사항을 버립니다. 그러나 충돌하지 않는 모든 로컬 변경 사항은 그대로 둡니다.
이것은 로컬 변경 사항과 충돌하는 병합 원본의 변경 사항을 버립니다. 그러나 로컬 변경 사항과 충돌하지 않는 모든 변경 사항은 유지합니다.
충돌을 해결됨으로 표시합니다. 이 버튼은 버튼을 사용하여 충돌을 수동으로 편집하고 변경 사항을 파일에 다시 저장하기 전까지는 비활성화됩니다. 변경 사항이 저장되면 버튼이 활성화됩니다.
병합 편집기를 시작하여 충돌을 수동으로 해결할 수 있습니다. 버튼이 활성화되도록 파일을 저장하는 것을 잊지 마십시오.
트리 충돌이 있는 경우, 먼저 다양한 유형의 트리 충돌과 발생 방식 및 이유에 대해 “트리 충돌” 섹션을 참조하십시오.
병합 후 트리 충돌을 해결하려면, 충돌 해결 방법에 대한 다양한 옵션이 있는 대화 상자가 표시됩니다.
다양한 트리 충돌 상황이 있을 수 있으므로, 대화 상자는 특정 충돌에 따라 이를 해결할 버튼을 표시합니다. 버튼 텍스트와 레이블은 충돌 해결 옵션이 무엇을 하는지 설명합니다. 확실하지 않다면, 대화 상자를 취소하거나 버튼을 사용하여 충돌을 나중에 해결하십시오.
별도의 브랜치에서 새 기능을 개발할 때, 기능이 완료되었을 때 재통합 정책을 수립하는 것이 좋습니다. 동시에 trunk에서 다른 작업이 진행 중이라면 시간이 지남에 따라 차이가 커져서 다시 병합하는 것이 악몽이 될 수 있습니다.
기능이 비교적 단순하고 개발이 오래 걸리지 않는다면, 기능이 완료될 때까지 브랜치를 완전히 분리한 다음 브랜치 변경 사항을 트렁크로 다시 병합하는 간단한 접근 방식을 채택할 수 있습니다. 병합 마법사에서는 이는 브랜치의 리비전 범위인 단순한 리비전 범위 병합(Merge a range of revisions)이 될 것입니다.
기능 개발에 더 오랜 시간이 걸리고 trunk의 변경 사항을 고려해야 한다면, 브랜치를 동기화된 상태로 유지해야 합니다. 이는 주기적으로 트렁크 변경 사항을 브랜치로 병합하여 브랜치가 모든 트렁크 변경 사항 과 새 기능을 포함하도록 하는 것을 의미합니다. 동기화 프로세스는 리비전 범위 병합(Merge a range of revisions)을 사용합니다. 기능이 완료되면 브랜치 재통합(Reintegrate a branch) 또는 두 개의 다른 트리 병합(Merge two different trees)을 사용하여 트렁크로 다시 병합할 수 있습니다.
트렁크에서 기능 브랜치로 모든 변경 사항을 병합하는 또 다른 (빠른) 방법은 확장 컨텍스트 메뉴(Shift 키를 누른 채 파일을 마우스 오른쪽 버튼으로 클릭)에서 → 을 사용하는 것입니다.
이 대화 상자는 매우 간단합니다. “병합 옵션” 섹션에 설명된 대로 병합 옵션을 설정하기만 하면 됩니다. 나머지는 TortoiseSVN이 병합 추적을 사용하여 자동으로 처리합니다.