본문 바로가기
TYLER SONGBlog
← Blog index
CS학습5 min read

Branch: 커밋을 가리키는 가벼운 포인터로 만드는 독립된 작업 흐름

Git의 브랜치(Branch)는 특정 커밋을 가리키는 이동 가능한 포인터이며, 파일을 복사하지 않고 41바이트 남짓한 참조 파일 하나로 만들어진다. 그래서 생성과 전환 비용이 작고, 기능 개발·버그 수정·실험을 서로 격리해 진행할 수 있다. 이 글에서는 브랜치의 내부 구조, 기본 명령어, 병합(merge)과 충돌 해결, 실무 협업 흐름을 다룬다.

By 송민성

1. 개념

브랜치(Branch)는 Git 저장소에서 특정 커밋(commit)을 가리키는 이동 가능한 포인터(pointer)다. 새 커밋을 만들면 현재 브랜치가 가리키는 위치가 새 커밋으로 자동으로 옮겨진다.

  • main(또는 master)은 특별한 브랜치가 아니라, 저장소를 만들 때 기본으로 생기는 브랜치 이름일 뿐이다.
  • HEAD는 "현재 내가 작업 중인 브랜치"를 가리키는 특수한 참조(reference)다.
  • 브랜치는 커밋 이력을 복사한 것이 아니라, 같은 커밋 그래프 위에 놓인 이름표다.

2. 왜 사용하는가

  • 작업 격리: 새 기능을 개발하는 중에도 main은 배포 가능한 상태로 유지할 수 있다.
  • 병렬 작업: 여러 사람이 서로의 작업에 영향을 주지 않고 동시에 개발한다.
  • 안전한 실험: 실패하면 브랜치만 삭제하면 되므로 부담이 적다.
  • 코드 리뷰 단위: 브랜치 하나가 풀 리퀘스트(Pull Request)의 단위가 된다.

3. 동작 원리

Git은 커밋을 부모 커밋을 가리키는 방향 비순환 그래프(DAG, Directed Acyclic Graph)로 저장한다. 브랜치는 이 그래프에서 커밋 하나의 해시를 담은 작은 텍스트 파일이다.

shell
cat .git/refs/heads/main # 커밋 해시 40자 + 개행 문자 (총 41바이트) cat .git/HEAD # ref: refs/heads/main

동작 과정은 다음과 같다.

  1. main이 커밋 C2를 가리키고, HEAD가 main을 가리킨다.
  2. git branch feature를 실행하면 feature라는 참조 파일이 C2를 가리키도록 생성된다. 파일 복사는 일어나지 않는다.
  3. git switch feature를 실행하면 HEAD가 feature를 가리키도록 바뀌고, 작업 디렉터리가 해당 커밋의 상태로 갱신된다.
  4. 이 상태에서 커밋하면 feature만 새 커밋 C3로 이동하고 main은 C2에 그대로 남는다.
text
C1 <- C2 <- C3   (feature, HEAD)
       ^
       main

병합(merge)은 두 브랜치의 변경을 합치는 작업이다.

  • 빨리감기 병합(fast-forward): 대상 브랜치가 현재 브랜치의 직계 후손이면, 포인터만 앞으로 옮기고 끝난다.
  • 3-way 병합: 두 브랜치가 갈라진 뒤 각각 커밋이 생겼다면, 공통 조상과 두 브랜치 끝을 비교해 병합 커밋(merge commit)을 만든다. 병합 커밋은 부모가 둘이다.
  • 같은 파일의 같은 부분을 서로 다르게 수정했다면 충돌(conflict)이 발생하고, 사람이 직접 해결해야 한다.

4. 코드 예제

아래 스크립트는 빈 임시 디렉터리에서 브랜치 생성부터 병합, 충돌 해결, 삭제까지 실행해 본다.

shell
#!/bin/zsh set -e dir=$(mktemp -d) cd "$dir" git init -b main git config user.name "Tester" git config user.email "tester@example.com" # 1) 초기 커밋 echo "hello" > app.txt git add app.txt git commit -m "init" # 2) 브랜치 생성 + 전환 (한 번에) git switch -c feature/greeting echo "hello, branch" > app.txt git commit -am "feat: change greeting" # 3) main으로 돌아와 같은 줄을 다르게 수정 → 충돌 유발 git switch main echo "hello, main" > app.txt git commit -am "fix: update greeting on main" # 4) 브랜치 그래프 확인 git log --oneline --graph --all # 5) 병합 시도 (충돌 발생, || true로 스크립트 중단 방지) git merge feature/greeting || true cat app.txt # <<<<<<< HEAD # hello, main # ======= # hello, branch # >>>>>>> feature/greeting # 6) 충돌 해결: 파일을 직접 고친 뒤 add + commit echo "hello, main and branch" > app.txt git add app.txt git commit -m "merge: resolve greeting conflict" # 7) 병합이 끝난 브랜치 삭제 git branch -d feature/greeting git branch

자주 쓰는 명령어는 다음과 같다.

shell
git branch # 로컬 브랜치 목록 git branch -vv # 각 브랜치의 마지막 커밋과 추적 중인 원격 브랜치 git switch <name> # 브랜치 전환 git switch -c <name> # 생성 후 전환 git branch -d <name> # 병합된 브랜치 삭제 git branch -D <name> # 병합 여부와 무관하게 강제 삭제 git branch -m <new-name> # 현재 브랜치 이름 변경 git push -u origin <name> # 원격에 올리고 추적 설정

5. 시간 복잡도 또는 성능 특성

  • 브랜치 생성/삭제: 참조 파일 하나를 만들거나 지우는 작업이므로 저장소 크기와 무관하게 사실상 일정한 비용이다.
  • 브랜치 전환: 참조 갱신 자체는 가볍지만, 두 브랜치 사이에 차이 나는 파일만큼 작업 디렉터리를 갱신해야 한다. 따라서 비용은 브랜치 수가 아니라 두 브랜치 간 변경된 파일 수에 비례한다.
  • 저장 공간: Git은 파일 내용을 객체(object)로 저장하고 같은 내용은 공유하므로, 브랜치를 만든다고 파일이 복제되지 않는다.

6. 실무 사용 사례

  • 기능 브랜치(feature branch): feature/login처럼 기능 단위로 브랜치를 만들고, 완료되면 풀 리퀘스트로 리뷰 후 main에 병합한다.
  • 핫픽스(hotfix): 운영 중 장애가 나면 main에서 hotfix/... 브랜치를 따서 빠르게 수정하고 배포한다.
  • 릴리스 브랜치: 배포 준비 기간에 release/1.2처럼 별도 브랜치를 두어 버그 수정만 받는다.
  • 브랜치 보호 규칙: GitHub·GitLab 등에서 main에 직접 푸시를 막고, CI 통과와 리뷰 승인을 병합 조건으로 건다.
  • 네이밍 규칙: feature/, fix/, chore/ 같은 접두사와 이슈 번호를 함께 쓰면 목록 파악이 쉽다.

7. 주의할 점

  • 커밋하지 않은 변경과 전환: 전환할 브랜치와 충돌하는 변경이 있으면 Git이 전환을 거부한다. 먼저 커밋하거나 git stash로 임시 보관한다.
  • detached HEAD: 브랜치가 아닌 특정 커밋을 체크아웃하면 HEAD가 브랜치 없이 커밋을 직접 가리킨다. 이 상태에서 만든 커밋은 브랜치를 만들지 않으면 찾기 어려워진다.
  • 오래 살아 있는 브랜치: 갈라진 기간이 길수록 충돌이 커진다. 작게 나누고 자주 main을 반영하는 편이 낫다.
  • `-D` 강제 삭제: 병합되지 않은 커밋이 있어도 삭제하므로 주의한다. 다만 직전 커밋 해시를 알면 git reflog로 복구할 수 있는 경우가 많다.
  • 로컬과 원격 브랜치는 별개: 로컬에서 삭제해도 원격 브랜치는 남는다. 원격 삭제는 git push origin --delete <name>으로 한다.
  • 공유 브랜치 이력 재작성 금지: 이미 푸시해서 다른 사람이 쓰는 브랜치에 rebase나 push --force를 쓰면 이력이 어긋난다.

8. 핵심 정리

  • 브랜치는 커밋 하나를 가리키는 포인터이고, 커밋할 때마다 앞으로 이동한다.
  • HEAD는 현재 브랜치를 가리키며, 브랜치 전환은 HEAD와 작업 디렉터리를 바꾸는 일이다.
  • 생성·삭제는 저장소 크기와 무관하게 가볍고, 전환 비용은 두 브랜치 간 차이 나는 파일 수에 좌우된다.
  • 병합은 빨리감기와 3-way 병합으로 나뉘며, 같은 부분을 달리 수정하면 충돌을 직접 해결해야 한다.
  • 실무에서는 짧게 유지하는 기능 브랜치, 보호된 main, 풀 리퀘스트 리뷰를 조합해 쓴다.