Git의 가장 강력한 기능 중 하나는 바로 브랜치(Branch) 를 활용한 유연한 작업 흐름 관리입니다. 브랜치를 사용하면 기본 코드(보통 main 또는 master 브랜치)에 영향을 주지 않으면서 새로운 기능을 개발하거나, 버그를 수정하거나, 다양한 실험을 안전하게 진행할 수 있습니다. 작업이 완료되면 이러한 변경 사항들을 다시 기본 브랜치로 병합(Merge) 하거나 리베이스(Rebase) 하여 통합합니다. 이 과정을 통해 여러 개발자가 동시에 각자의 작업을 효율적으로 진행하고, 프로젝트를 안정적으로 관리할 수 있습니다.
브랜치 생성 및 전환 (git branch, git checkout, git switch)
새로운 작업 흐름을 만들기 위해서는 먼저 브랜치를 생성해야 합니다. 예를 들어, 현재 안정된 main 브랜치에서 새로운 로그인 기능을 개발하기 위해 feature-login
이라는 독립적인 작업 공간(브랜치)을 만들고 싶을 수 있습니다.git branch <새_브랜치_이름>
명령어는 새로운 브랜치를 생성합니다.
# 'feature-login'이라는 이름의 새 브랜치를 생성
git branch feature-login
이 명령어는 브랜치만 생성할 뿐, 현재 작업 중인 브랜치를 새로 만든 브랜치로 옮기지는 않습니다. 생성된 feature-login
브랜치로 이동하여 실제 로그인 기능 개발 작업을 시작하고 싶다면, git checkout <브랜치_이름>
명령어를 사용해야 합니다.
# 'feature-login' 브랜치로 현재 작업 브랜치를 전환
git checkout feature-login
브랜치를 생성하고 즉시 해당 브랜치로 전환하는 작업을 한 번에 하고 싶을 때도 있습니다. 예를 들어, 급하게 홈페이지의 작은 문구를 수정해야 하는 ‘hotfix-typo’ 작업을 위해 새 브랜치를 만들고 바로 그 브랜치에서 작업을 시작해야 한다면 git checkout -b <새_브랜치_이름>
명령어가 매우 유용합니다.
# 'hotfix-typo'라는 새 브랜치를 만들고 즉시 해당 브랜치로 전환
git checkout -b hotfix-typo
최근 Git 버전에서는 브랜치 전환을 위한 보다 명확한 명령어로 git switch가 도입되었습니다. 기존의 git checkout
은 브랜치 전환 외에도 다른 기능(파일 되돌리기 등)을 함께 수행하여 혼동을 줄 수 있었지만, git switch
는 오직 브랜치 전환에만 사용됩니다.
# 'feature-login' 브랜치로 전환 (git checkout feature-login 과 동일)
git switch feature-login
# 'feature-ui-enhancement'라는 새 브랜치를 만들고 즉시 해당 브랜치로 전환
git switch -c feature-ui-enhancement
-c
옵션은 create의 약자로, 새로운 브랜치를 만들고 전환하는 역할을 합니다.
브랜치 목록 확인 (git branch)
프로젝트를 진행하다 보면 현재 내 로컬 저장소에 어떤 브랜치들이 있는지, 그리고 내가 지금 어떤 브랜치에서 작업 중인지 확인해야 할 때가 많습니다. 이때 git branch
명령어를 사용합니다. 이 명령어를 실행하면 로컬 브랜치들의 목록이 나타나며, 현재 작업 중인 브랜치 앞에는 별표(*
)가 표시됩니다.
# 로컬 브랜치 목록 확인 (현재 브랜치는 '*'로 표시됨)
git branch
# 원격 저장소에 있는 브랜치(정확히는 원격 추적 브랜치) 목록 확인
git branch -r
# 로컬 브랜치와 원격 추적 브랜치 모두 확인
git branch -a
예를 들어, 동료가 원격 저장소에 새로운 기능 브랜치를 푸시했는지 확인하고 싶거나, 로컬에 너무 많은 작업 브랜치가 쌓여 정리가 필요한지 파악하고 싶을 때 git branch -a
또는 git branch
를 유용하게 사용할 수 있습니다.
브랜치 병합 (git merge)
독립된 브랜치에서 기능 개발이나 버그 수정 작업이 완료되면, 그 변경 사항들을 주 개발 라인이나 다른 통합 브랜치로 합쳐야 합니다. 이 과정을 병합(Merge) 이라고 하며, git merge <병합할_브랜치_이름>
명령어를 사용합니다. 예를 들어, feature-login
브랜치에서 로그인 기능 개발을 완료하고 충분히 테스트한 후, 이 변경 사항들을 주 개발 라인인 main
브랜치에 통합하고 싶다면, 먼저 main
브랜치로 이동(git checkout main
또는 git switch main
)한 다음 git merge feature-login
명령을 실행합니다.
# 1. 병합의 대상이 될 브랜치로 먼저 이동 (예: main 브랜치)
git switch main
# 2. 'feature-login' 브랜치의 변경 내용을 현재 브랜치(main)로 병합
git merge feature-login
병합에는 크게 두 가지 방식이 있습니다:
Fast-forward 병합:
만약 main
브랜치에서 feature-login
브랜치를 만든 이후 main
브랜치에 아무런 추가 커밋이 없었다면, feature-login
브랜치를 병합할 때 Git은 단순히 main
브랜치 포인터를 feature-login
브랜치의 최신 커밋으로 이동시킵니다. 이처럼 대상 브랜치가 병합될 브랜치의 직접적인 조상인 경우 발생하는 병합을 Fast-forward 병합이라고 하며, 이 경우 별도의 병합 커밋이 생성되지 않아 히스토리가 단순하게 유지됩니다.
3-way 병합 (Non-fast-forward 병합):
main
브랜치에서 feature-login
브랜치를 분기한 후, main
브랜치에도 다른 변경사항(예: 긴급 버그 수정)이 커밋되었고, feature-login
브랜치에도 로그인 기능 관련 커밋들이 쌓였다면, 이 두 브랜치는 서로 다른 작업 이력을 가지게 됩니다. 이때 main
브랜치에서 git merge feature-login
을 실행하면 Git은 두 브랜치의 공통 조상, 그리고 각 브랜치의 최신 커밋, 이렇게 세 지점(3-way)을 기반으로 변경사항을 통합하고 새로운 병합 커밋(merge commit) 을 생성합니다. 이 병합 커밋은 어떤 브랜치들이 합쳐졌는지 명확히 보여주는 역할을 합니다.
병합 충돌 (Merge Conflict) 해결
때로는 서로 다른 브랜치에서 같은 파일의 같은 부분을 각기 다르게 수정했을 경우, Git은 자동으로 어떤 변경사항을 선택해야 할지 판단할 수 없어 병합 충돌(Merge Conflict) 이 발생합니다. 예를 들어, main
브랜치와 feature-header
브랜치에서 모두 index.html 파일의 <title>
태그 내용을 서로 다르게 수정했다면, main
브랜치에서 git merge feature-header
를 시도할 때 병합 충돌이 발생합니다.
이 경우, Git은 충돌이 발생한 파일에 <<<<<<<
, ₩=======
, >>>>>>>
와 같은 특별한 마커를 삽입하여 어느 브랜치에서 어떤 내용이 변경되었는지 표시해 줍니다. 개발자는 다음 단계를 통해 충돌을 해결해야 합니다
git status
명령으로 어떤 파일에서 충돌이 발생했는지 확인합니다.- 충돌이 발생한 파일을 직접 열어, Git이 표시한 마커를 참고하여 두 브랜치의 변경 내용을 비교하고, 최종적으로 사용할 코드를 결정하여 파일을 수정합니다. 충돌 마커(
<<<<<<< HEAD
,₩=======
,>>>>>>> other_branch
)도 모두 제거해야 합니다. - 수정이 완료된 파일을
git add <충돌_해결한_파일명>
명령으로 스테이징하여 Git에게 충돌이 해결되었음을 알립니다. - 모든 충돌을 해결하고 스테이징했다면,
git commit
명령으로 병합을 최종 완료합니다. Git은 보통 병합 커밋 메시지를 자동으로 제안해 줍니다.
만약 병합 과정이 너무 복잡하거나 잘못되었다고 판단되면, git merge --abort
명령으로 병합 시도 자체를 취소하고 이전 상태로 돌아갈 수 있습니다.
브랜치 삭제 (git branch -d, git branch -D)
작업이 완료되어 주 브랜치에 성공적으로 병합된 브랜치나, 더 이상 필요 없어진 브랜치는 삭제하여 저장소를 깔끔하게 관리하는 것이 좋습니다. 예를 들어, feature-login
브랜치의 변경 사항이 성공적으로 main
브랜치에 병합되었고, 더 이상 해당 브랜치가 필요 없다고 판단될 때 로컬 브랜치를 삭제할 수 있습니다.git branch -d <삭제할_브랜치_이름>
명령어는 병합된 로컬 브랜치를 삭제합니다.
# 'feature-login' 브랜치가 이미 다른 브랜치에 병합되었다면 삭제
git branch -d feature-login
이 명령어는 해당 브랜치의 변경 사항이 다른 브랜치에 병합되지 않았다면, 데이터 유실을 방지하기 위해 삭제를 거부하여 비교적 안전합니다.
하지만 만약 experiment-feature
라는 브랜치에서 실험적인 기능을 개발하다가 이 기능이 필요 없어져서 병합하지 않고 강제로 삭제하고 싶을 때는 git branch -D <삭제할_브랜치_이름>
(대문자 D) 명령어를 사용합니다.
# 'experiment-feature' 브랜치를 병합 여부와 관계없이 강제 삭제
git branch -D experiment-feature
이 경우 해당 브랜치의 커밋들은 유실될 수 있으므로, 정말 필요 없는 브랜치인지 신중하게 확인한 후 사용해야 합니다.
리베이스 (git rebase)
리베이스(Rebase) 는 한 브랜치에서 이루어진 일련의 커밋들을 복사하여 다른 브랜치의 최신 커밋 위로 옮겨와 마치 그 브랜치에서 작업을 시작한 것처럼 히스토리를 재정렬하는 과정입니다. 이는 브랜치의 시작점(base)을 변경(re-base)한다는 의미를 가집니다. 리베이스를 사용하면 프로젝트 히스토리를 보다 선형적이고 깔끔하게 만들 수 있다는 장점이 있습니다.
예를 들어, feature-A
브랜치에서 작업을 진행하는 동안 main
브랜치에는 여러 중요한 업데이트가 반영되었을 수 있습니다. feature-A
를 main에
병합하기 전에, main
브랜치의 최신 변경사항들을 feature-A
브랜치에 먼저 반영하여 마치 feature-A
가 최신 main에서
시작된 것처럼 히스토리를 정리하고 싶을 때 리베이스를 사용할 수 있습니다. 이를 위해 먼저 feature-A
브랜치로 이동한 후, git rebase main
명령을 실행합니다.
# 1. 리베이스 대상 브랜치로 이동 (예: feature-A)
git switch feature-A
# 2. 현재 브랜치(feature-A)의 커밋들을 'main' 브랜치의 최신 커밋 위로 재배치
git rebase main
이 과정이 성공적으로 완료되면 feature-A
브랜치의 커밋들은 기존의 main
브랜치에서 분기되었던 시점이 아닌, 현재 main
브랜치의 가장 최신 커밋 바로 뒤에 이어지게 됩니다.
Merge와의 차이점 및 사용 시나리오
|병합(Merge)|리베이스(Rebase)|
|-|-|
|두 브랜치의 이력을 그대로 유지하면서, 변경 사항을 통합하기 위한 새로운 병합 커밋을 생성합니다 (non-fast-forward의 경우). 히스토리가 실제 발생한 대로 기록되며, 어떤 브랜치에서 작업이 진행되었는지 명확히 보여줍니다.|커밋들을 새로운 기준(base) 위에 다시 적용하여 히스토리를 선형적으로 재작성합니다. 프로젝트 히스토리가 간결해지고 따라가기 쉬워질 수 있지만, 원래 브랜치가 언제, 어떤 상태에서 분기되었는지에 대한 정보는 변경됩니다.|
⚠️ 리베이스 사용 시 주의할 점
절대로 여러 사람이 함께 사용하는 원격 브랜치(예: origin/main
, origin/develo
p)에 이미 푸시된 커밋에 대해서는 리베이스를 사용하지 마세요. 리베이스는 기존 커밋을 새로운 커밋으로 대체하는 것이므로, 다른 팀원이 변경된 히스토리를 기반으로 작업하고 있다면 심각한 혼란과 문제를 야기할 수 있습니다. 리베이스는 주로 개인 로컬 브랜치를 정리하거나, 원격 저장소에 푸시하기 전 개인 작업 내용을 깔끔하게 만들고 싶을 때 유용합니다. 공유 브랜치의 변경 사항을 가져올 때는 git merge
나 git pull
(내부적으로 merge
또는 rebase
사용 가능)을 사용하는 것이 더 안전합니다.
Rebase 중 충돌 해결
리베이스 과정에서도 병합 충돌과 유사한 충돌이 발생할 수 있습니다. 리베이스는 각 커밋을 순서대로 대상 브랜치 위에 다시 적용하려고 시도하기 때문에, 각 커밋 단위로 충돌이 발생할 수 있습니다. feature-A
브랜치를 main
브랜치 위로 리베이스하는 과정에서, main
브랜치의 변경사항과 feature-A
의 특정 커밋이 동일한 파일의 같은 부분을 수정했다면 충돌이 발생합니다.
Git은 리베이스를 중단하고 충돌 해결을 요청합니다. 충돌 해결 과정은 다음과 같습니다:
- 충돌이 발생한 파일을 열어 수동으로 내용을 수정하고 충돌 마커를 제거합니다.
- 수정한 파일을
git add <충돌_해결한_파일명>
명령으로 스테이징합니다. git rebase --continue
명령을 실행하여 리베이스 과정을 계속 진행합니다.
이 과정은 리베이스할 남은 커밋들에 대해 충돌이 발생할 때마다 반복될 수 있습니다.
만약 특정 커밋 적용 중 발생한 충돌을 해결하기 어렵거나 해당 커밋을 건너뛰고 싶다면 git rebase –skip을 사용할 수 있고, 전체 리베이스 과정을 중단하고 싶다면 git rebase –abort를 사용할 수 있습니다.
마무리
브랜치와 병합, 그리고 리베이스는 Git의 강력한 협업 및 버전 관리 기능을 뒷받침하는 핵심 요소들입니다. 각 기능의 특성과 사용 시나리오를 잘 이해하고 적절히 활용한다면, 복잡한 프로젝트도 체계적이고 효율적으로 관리할 수 있게 될 것입니다.