A 브랜치에서 해야 했던 수정 및 커밋을 실수로 B 브랜치에 하거나, 다른 브랜치에서 한 특정 커밋을 이 브랜치에도 가져오고 싶은 경우가 생길 수 있다. branch merge란 이렇게 다른 브랜치에서 했던 커밋을 가져오고 싶을 때 사용하게 되는 방법으로, merge
커맨드를 이용한다.
업데이트가 필요한 브랜치에서 git merge [적용하고 싶은 커밋이 있는 브랜치 이름]
명령을 사용하면 되고, conflict가 있을 경우 conflict를 해결한 후 커밋한다.
branch merge를 하면 보통 merge 커밋을 새로 생성하게 되지만, 예외도 있다.
Fast-forward merge는 커밋 히스토리에서 같은 선상에 있는 branch를 merge하는 경우를 의미한다. 즉 branch 자체는 A와 B로 분리되었으나, 분리된 이후 A와 B가 각각 서로 다른 내용으로 업데이트 된 것이 아니라 A 또는 B 둘 중에 한 branch만 새 커밋을 만들어 갔을 경우이다.
이 경우에는 새로운 merge 커밋을 생성하지 않고, 단순히 앞선 branch가 가리키고 있는 커밋을 뒤에 있던 branch와 head가 가리키게 된다. 약간의 시청각 요소가 있어야 이해하기 쉬울 것 같은데, 결과적으로 그냥 커밋을 이동한 것과 같은 결과가 된다. 구조상 새로운 커밋을 만들 필요가 없기 때문에 이처럼 처리되는 것 같다!
3-way merge는 일반적으로 생각하는 각각 뻗어나간 가지 모양의 두 branch를 merge하는 방식이다. 두 branch가 커밋 히스토리 상에서 분리된 2개의 선에 각각 존재할 때 merge하게 되면 merge 커밋을 새로 생성하게 된다.
3-way merge의 결과는 base(분리 전)를 기준으로, branch 분리 이후에 생긴 새로운 변화가 있는 쪽(수정 사항이 있는 쪽)을 적용하며, 만약 branch A와 B가 동일한 부분에 수정사항이 있을 경우 conflict를 발생시켜 사용자가 원하는 최종 사항을 선택하게끔 한다.
놀랍게도 위클리 페이퍼의 출제 의도를 벗어난 답변을 했기 때문에...
PR merge에 대한 질문이었다고 한다 (ㅠㅠ)
따라서 아래의 내용을 추가한다.
PR merge는 Pull Request Merge, 즉 GitHub 내에서 사용자들에게 작업 내용을 검토받기 위해 올린 PR이 승인되었을 경우의 merge를 의미하는데 총 3가지 방법이 있다.
아주 단순하고 기본적인 merge 방식이다. 병합하는 두 branch의 변경사항 및 가지 모양을 모두 유지하고, 새로운 merge commit을 만들어 병합한 모양까지 보여주는 방식이다. 각각의 작업 히스토리를 모두 유지하면서 병합하기 때문에, 진행 상황을 이해하거나 문제가 발생해도 추적하기 쉽다. 당연히 commit 해시도 변경되지 않고 그에 따른 문제 발생도 없다.
단점은 히스토리가 복잡해진다는 것이다. 생성한 branch가 많고 프로젝트가 대규모일수록 복잡성이 커지기 때문에 관리하기 어려울 수 있다.
A branch에서 생성한 모든 변경 사항을 본 branch인 B branch에 하나의 commit으로 압축하여 생성한다. 즉, A branch에서 commit을 A1, A2, A3, ...과 같이 많이 생성해두었더라도, Squash and merge 방식을 선택하면 B branch에 A.zip
처럼 하나의 commit으로 merge된다. 그리고 A branch는 사라지게 된다.
히스토리를 간단하게 유지할 수 있고, 이렇게 생성된 commit은 각각 한 PR의 내용을 대변하게 되므로 해당 branch의 가장 중요한 내용만 압축하여 빠르게 이해할 수 있다.
그러나 작업의 상세 이력을 잃게 된다는 단점이 있다. 모든 commit을 하나로 압축해버리기 때문에 세부적으로 어떤 작업을 누가 했는지나, 각 commit의 개별적인 맥락들(어떤 문제가 있었는지, 뭘 추가했는지 등)을 잃게 된다. 따라서 문제가 발생했을 때 해결이 어려울 수 있다.
또한 commit 해시에 변경이 발생하기 때문에(기존 작업 commit이 하나의 해시로 합쳐지며 사라짐) 한 branch에서 여러명이 동시에 작업을 하고 있던 경우 문제가 발생할 수 있다.
A branch의 모든 변경 사항(commit들)을 B branch에 재배치시켜 병합한다. 즉, B branch 위로 A branch의 모든 commit을 옮겨 선형적인 모습을 만든다. B1 - B2 - B3 형태의 B branch가 있고, B2에서 A branch를 생성하여 A1 - A2 - A3 형태로 commit을 만들어갔을 경우, Rebase and merge를 B branch 기준으로 실행한다면 B1 - B2 - B3 - A1' - A2' - A3' 형태로 병합된다. 깨끗하고 선형적인 히스토리를 만들 수 있어, 히스토리 파악 및 코드 변화 이해가 쉽다는 장점이 있다.
그러나 위에서 예시를 든 바와 같이 "A1 - A2 - A3"이 "A1' - A2' - A3'"으로, 즉 병합되는 branch 각각의 commit 해시가 변화하게 된다. 따라서 여러명이 동시에 작업을 하고 있을 경우 복잡한 충돌이 발생할 가능성이 있다.
또, PR 별로 나뉘어있던 작업 이력이 하나의 선형 히스토리로 합쳐지기 때문에 어디서부터 어디까지가 특정 기능을 위한 commit인지 구분하기 어려워진다는 단점이 있다.
branch는 여러 기능을 여러 사람이 병렬적으로 개발할 수 있게 한다. 하나의 main을 유지하면서 main branch의 다른 버전(예: 무료/업그레이드 버전)을 만들 수도 있고, 또 다른 branch에서는 기능 개발을 하는 등 main branch에 영향을 주지 않으면서도 다양한 도전을 시도하고 폐기할 수 있다.
Git branch 전략은 이런 branch를 효과적으로 관리하기 위한 워크플로우로, 프로젝트, 팀, 회사 단위로 어떤 branch를 생성하고, 해당 branch의 목적은 무엇이며, 어디에서 분기하며, 어디서 병합할지 등에 대한 규칙을 정하고 지키는 것을 의미한다.
이런 Git branch 전략 중에서 대중적으로 사용되는 것이 Vincent Driessen이 2010년에 제시한 Git Flow이다.
Git Flow는 branch를 크게 5개로 구분하여 관리한다.
지속적으로 프로세스 전반에 걸쳐 유지되는(무한한 수명을 가진) Main branches로 Main(구 master)과 Develop가 있고, 목적을 다하면 삭제되는 Supporting branches로 Feature, Release, Hotfix가 있다. 각각의 설명은 아래와 같다.
Main branch
-출시 가능한(배포 가능한) 프로덕션 코드를 모아두는 branch
-시작시 생성되는, 기준이 되는 branch
-배포된 각 버전을 Tag를 이용해 표시
Develop branch
-다음 버전 개발을 위한 코드를 모아두는 branch
-개발이 완료되면 Main으로 merge(기능 업데이트)
Feature branch
-하나의 기능을 개발하기 위한 branch
-Develop branch에서 생성하며, 개발 완료되면 Develop branch로 merge
-merge 커밋을 생성하며 merge해야 함 (Fast-forward merge하지 않음)
Release branch
-소프트웨어 배포를 준비하기 위한 branch
-Develop branch에서 생성하며
-버전 이름 등의 소소한 데이터나 사소한 버그를 수정하기 위해 사용(QA)
-배포 준비가 완료되면 Main과 Develop에 둘 다 merge
Hotfix branch
-이미 배포된 버전(Main)에 문제가 생겼을 경우 빠른 해결을 위해 생성
-Main branch에서 생성
-문제 해결이 완료되면 Main과 Develop에 둘 다 merge
Git Flow는 명시적으로 버전 관리가 필요한 어플리케이션, 오픈소스 라이브러리/프레임워크 등의 프로젝트엔 적합하지만 웹 어플리케이션에는 다소 부적합하다. 웹 어플리케이션의 특성상 항상 단일이자 최신인 버전만을 사용하게 되기 때문이다.
따라서 웹 어플리케이션 같은 경우는 더 단순한 워크플로우인 Github Flow를 채택할 것을 권하고 있다. Github Flow는 Main과 다른 하나(topic, 새로운 기능)라는 두 가지 branch만을 사용하고, Main은 언제든 배포가 가능한 상태를 유지해야 한다.
Main이 아닌 다른 branch에는 엄격한 규칙을 적용하지 않는다. 새로운 기능을 위한 topic 브랜치는 Main branch에서 생성하고, 기능을 설명하는 명확한 이름으로 네이밍한다. 기능이 완성되지 않았더라도 수시로 push
하여 다른 팀원과 커뮤니케이션한다.
merge 준비가 완료되면 PR하고, 모든 준비가 완료되었다면 Main에 반영한다.