06. 브랜치(Branch) — 평행 세계에서 작업하기

🎯 이 장의 목표
  • 브랜치가 무엇이고 왜 Git의 핵심 기능인지 이해한다
  • 브랜치를 만들고(branch), 이동하고(switch/checkout), 삭제한다
  • HEAD 포인터가 브랜치와 어떻게 움직이는지 그림으로 이해한다
  • 로컬 브랜치를 원격에 올리고 업스트림으로 연결한다

6.1 브랜치란 무엇인가?

게임의 "평행 세계(멀티버스)"를 떠올려 보세요. 같은 출발점에서 시작했지만, 한쪽 세계에서는 A 선택을, 다른 세계에서는 B 선택을 합니다. 두 세계는 서로 영향을 주지 않습니다.

브랜치(Branch)는 바로 이 "평행 세계"입니다. 메인 코드에 영향을 주지 않고, 별도의 공간에서 자유롭게 실험하고 개발할 수 있게 해줍니다.

gitGraph
    commit
    commit
    branch feature
    checkout feature
    commit
    commit
    checkout main
main(안정 버전)에서 갈라져 나온 feature(새 기능 실험)가 별도로 자라납니다.

브랜치가 왜 필요할까?

  • 안전한 실험: 새 기능을 만들다 망쳐도 main(원본)은 멀쩡합니다.
  • 동시 작업: 동료 A는 로그인 기능, 동료 B는 결제 기능을 각자 브랜치에서 동시에 개발합니다.
  • 버그 수정 분리: 긴급 버그는 별도 브랜치에서 고쳐 빠르게 반영합니다.
🌳 나무 비유
main이 굵은 줄기라면, 브랜치는 거기서 뻗어 나온 가지입니다. 가지에서 열매(기능)를 충분히 키운 뒤, 다시 줄기에 붙입니다(병합). "branch"가 영어로 "나뭇가지"인 이유죠.

6.2 브랜치의 정체 — 사실은 그냥 "포인터"

많은 입문자가 브랜치를 "코드 전체의 복사본"이라고 오해합니다. 틀렸습니다.

브랜치는 그저 특정 커밋을 가리키는 가벼운 이름표(포인터)일 뿐입니다. 새 브랜치를 만들어도 코드가 복사되지 않습니다. 단지 41바이트(바이트는 데이터 크기 단위로, 41바이트는 한글 13~14자 정도의 아주 작은 크기)짜리 작은 파일(커밋 해시를 담은) 하나가 생길 뿐입니다. 그래서 Git의 브랜치는 엄청나게 가볍고 빠릅니다.

flowchart RL
    C["커밋C"] --> B["커밋B"] --> A["커밋A"]
    main(["🏷️ main"]) -.-> C
    classDef ptr fill:#fde68a,stroke:#d97706,color:#92400e
    classDef commit fill:#dcfce7,stroke:#16a34a,color:#166534
    class main ptr
    class A,B,C commit
main은 "커밋C를 가리키는 이름표"일 뿐입니다.

이 점이 SVN 같은 옛 도구와 Git의 결정적 차이이자, Git이 사랑받는 이유입니다.

6.3 기본 브랜치: main (또는 master)

저장소를 처음 만들면 기본 브랜치 하나가 자동으로 생깁니다.

  • 예전 기본 이름: master
  • 요즘 기본 이름: main (GitHub 등 대부분이 전환)

둘은 이름만 다를 뿐 기능은 같습니다. 2장에서 설정한 init.defaultBranch main 덕분에 새 저장소는 main으로 시작합니다.

BASH
# 현재 브랜치 목록 보기 (현재 위치는 * 표시)
git branch
CODE
* main

6.4 브랜치 만들기 — git branch

BASH
# feature 라는 이름의 브랜치 생성
git branch feature

이것만으로는 브랜치를 만들기만 하고, 이동하지는 않습니다. 확인해봅시다.

BASH
git branch
CODE
  feature
* main          ← 여전히 main에 있음 (* 가 현재 위치)
flowchart RL
    C["커밋C"] --> B["커밋B"] --> A["커밋A"]
    main(["🏷️ main"]) -.-> C
    feature(["🏷️ feature"]) -.-> C
    HEAD(["🔖 HEAD"]) -.-> main
    classDef head fill:#fde68a,stroke:#d97706,color:#92400e
    classDef ptr fill:#fed7aa,stroke:#ea580c,color:#9a3412
    classDef commit fill:#dcfce7,stroke:#16a34a,color:#166534
    class HEAD head
    class main,feature ptr
    class A,B,C commit
mainfeature 둘 다 커밋C를 가리키고, HEAD는 아직 main을 가리킵니다. (브랜치만 만들고 이동은 안 한 상태)

브랜치 이름 규칙

좋은 브랜치 이름은 협업을 부드럽게 만듭니다.

좋은 예 ✅설명
feature/login기능 개발 (슬래시로 분류)
bugfix/cart-crash버그 수정
hotfix/security-patch긴급 수정
release/v1.2.0배포 준비

규칙:

  • 공백 대신 하이픈(-)이나 슬래시(/) 사용
  • 소문자 권장, 의미를 알 수 있게
  • 한글·특수문자(~ ^ : ? * [ 등)는 피하기

6.5 브랜치 이동하기 — git switch / git checkout

만든 브랜치로 이동(전환)해봅시다.

BASH
# 신버전 (권장, Git 2.23+)
git switch feature

# 구버전 (여전히 널리 쓰임)
git checkout feature
CODE
Switched to branch 'feature'
BASH
git branch
CODE
* feature       ← 이제 feature로 이동함!
  main
flowchart RL
    C["커밋C"] --> B["커밋B"] --> A["커밋A"]
    main(["🏷️ main"]) -.-> C
    feature(["🏷️ feature"]) -.-> C
    HEAD(["🔖 HEAD"]) -.-> feature
    classDef head fill:#fde68a,stroke:#d97706,color:#92400e
    classDef ptr fill:#fed7aa,stroke:#ea580c,color:#9a3412
    classDef commit fill:#dcfce7,stroke:#16a34a,color:#166534
    class HEAD head
    class main,feature ptr
    class A,B,C commit
git switch featureHEAD가 feature를 따라 이동했습니다. (커밋 위치는 아직 그대로)
💡 팁
switch vs checkout
과거에는 checkout 하나가 "브랜치 이동"과 "파일 되돌리기" 두 가지 일을 모두 해서 헷갈렸습니다. 그래서 Git 2.23부터 역할을 나눴습니다.
  • git switch → 브랜치 이동 전용
  • git restore → 파일 되돌리기 전용

Git 3.0에서는 이 둘이 정식 권장 방식이 됩니다. 이 책은 둘 다 보여주되 신버전을 권장합니다.

생성과 이동을 한 번에

BASH
# 신버전: -c (create)
git switch -c feature2

# 구버전: -b (branch)
git checkout -b feature2

feature2를 만들면서 동시에 그곳으로 이동합니다. 실무에서 가장 많이 쓰는 형태입니다.

이전 브랜치로 빠르게 돌아가기

BASH
git switch -      # 바로 직전에 있던 브랜치로 (checkout - 도 동일)

6.6 브랜치에서 커밋하면 어떻게 될까? (핵심 그림)

feature 브랜치로 이동한 상태에서 커밋해봅시다.

BASH
git switch feature
echo "new feature code" > feature.txt
git add feature.txt
git commit -m "Add feature.txt"

이제 그림이 갈라집니다.

gitGraph
    commit id: "A"
    commit id: "B"
    commit id: "C"
    branch feature
    checkout feature
    commit id: "D (feature 전진)"
feature는 새 커밋 D로 전진했지만, main은 커밋 C에 그대로 머뭅니다.
  • feature는 새 커밋D로 전진했습니다.
  • main은 커밋C에 그대로 있습니다. main은 전혀 영향받지 않았습니다.

이것이 브랜치의 핵심입니다. main으로 돌아가면 feature.txt는 보이지도 않습니다.

BASH
git switch main
ls           # feature.txt 가 없음! (main 세계에는 존재하지 않으니까)
🤯 처음엔 "파일이 사라졌다!"고 놀랄 수 있지만, 사라진 게 아니라 다른 평행 세계(feature)에 있는 것입니다. git switch feature 하면 다시 나타납니다.

6.7 HEAD 포인터 깊이 이해하기

HEAD는 "현재 내가 어느 브랜치에 있는지"를 가리키는 포인터입니다. 정확히는 "현재 브랜치를 가리키는 포인터"입니다.

CODE
HEAD → feature → (커밋D)
        main   → (커밋C)
  • git switch로 브랜치를 바꾸면 → HEAD가 다른 브랜치를 가리킴
  • 그 브랜치에서 커밋하면 → 브랜치 포인터와 HEAD가 함께 전진

상대적 위치 표기

HEAD를 기준으로 "몇 칸 뒤" 커밋을 가리킬 수 있습니다.

BASH
HEAD        # 현재 커밋
HEAD~1      # 현재 커밋의 바로 이전 (부모)
HEAD~2      # 두 단계 이전
HEAD^       # 부모 (~1과 유사)

이 표기는 뒤의 reset/rebase 장에서 매우 유용하게 쓰입니다.

소스트리에서는 HEAD가 현재 어느 커밋·브랜치에 있는지 그래프에서 굵게 강조되어 직관적으로 보입니다.

6.8 특정 커밋으로 이동 — Detached HEAD

브랜치가 아닌 특정 커밋으로 직접 이동할 수도 있습니다.

BASH
git switch --detach a1b2c3d
# 또는
git checkout a1b2c3d

이때 "detached HEAD(분리된 HEAD)" 상태가 됩니다. 어떤 브랜치에도 속하지 않고 커밋 위에 둥둥 떠 있는 상태입니다.

CODE
Note: switching to 'a1b2c3d'.
You are in 'detached HEAD' state...
⚠️ 흔한 실수
detached HEAD 주의: 이 상태에서 한 커밋은 어느 브랜치에도 연결되지 않아, 다른 곳으로 이동하면 잃어버리기 쉽습니다. 과거 코드를 잠깐 구경하는 용도로만 쓰고, 여기서 계속 작업하려면 git switch -c new-branch로 새 브랜치를 만들어 안착시키세요.

6.9 브랜치 전환 전, 워킹 디렉터리 정리

커밋하지 않은 변경이 있는 상태에서 브랜치를 바꾸려 하면, Git이 막을 수 있습니다.

CODE
error: Your local changes to the following files would be overwritten by checkout...

이는 "이 변경을 잃을 수 있으니, 먼저 처리하라"는 경고입니다. 해결 방법은 두 가지입니다.

  1. 커밋하기 — 변경이 의미 있다면 커밋 후 전환
  2. 스태시하기 — 아직 커밋하긴 이르다면 임시 보관 (→ 다음 장 07)

브랜치를 바꾸기 전에는 워킹 디렉터리를 깨끗하게(git status가 clean) 유지하는 것이 좋은 습관입니다.

6.10 원격 브랜치 다루기

로컬에서 만든 브랜치는 아직 내 컴퓨터에만 있습니다. 동료와 공유하려면 원격에 올려야 합니다.

로컬 브랜치를 원격에 올리기

BASH
# feature 브랜치를 origin에 올리고 업스트림 연결(-u)
git push -u origin feature

-u 덕분에 로컬 feature와 원격 origin/feature가 연결됩니다. 이후로는 그냥 git push/git pull만 쳐도 됩니다.

원격에 어떤 브랜치가 있는지 보기

BASH
git branch -r          # 원격 브랜치만
git branch -a          # 로컬 + 원격 전체
CODE
  feature
* main
  remotes/origin/feature
  remotes/origin/main

원격 브랜치를 로컬로 가져오기

동료가 원격에 올린 브랜치를 내 로컬에서 작업하려면:

BASH
git fetch origin                       # 원격 정보 갱신
git switch feature                     # 같은 이름이면 자동 추적 연결됨

최신 Git은 원격에만 있는 브랜치 이름으로 switch하면 자동으로 로컬 브랜치를 만들고 추적까지 연결해줍니다.

이름이 다른 브랜치 연결 / 업스트림 설정

BASH
# 이미 있는 로컬 브랜치에 업스트림을 지정
git branch --set-upstream-to=origin/feature feature

# 또는 push 시점에 설정
git push -u origin 로컬브랜치명:원격브랜치명
📌 핵심
업스트림 트래킹 정리: "이 로컬 브랜치가 따라갈 원격 브랜치"를 지정하는 것입니다. 연결돼 있으면 git status가 "origin보다 2개 앞섬" 같은 정보를 알려주고, push/pull이 목적지를 자동으로 압니다.

6.11 브랜치 삭제하기

작업이 끝나(보통 병합 후) 더 이상 필요 없는 브랜치는 정리합니다.

BASH
# 안전 삭제: 병합되지 않은 변경이 있으면 거부됨
git branch -d feature

# 강제 삭제: 병합 여부 무관하게 삭제 (주의)
git branch -D feature
옵션의미
-d (소문자)안전 삭제. 병합 안 된 커밋이 있으면 막아줌
-D (대문자)강제 삭제. 경고 무시하고 삭제
⚠️ 흔한 실수
-D로 삭제한 브랜치의 미병합 커밋은 잃어버리기 쉽습니다. 정말 버려도 되는지 확인하세요.

원격 브랜치 삭제

BASH
git push origin --delete feature
# 또는
git push origin :feature

소스트리에서는 좌측 브랜치 목록에서 우클릭 → Delete로 삭제하며, 미병합 브랜치는 경고를 띄워줍니다.

6.12 실습: 브랜치 전체 사이클

BASH
# 1. 새 기능 브랜치 만들고 이동
git switch -c feature/greeting

# 2. 작업 & 커밋
echo "Hello!" > greeting.txt
git add greeting.txt
git commit -m "Add greeting message"

# 3. 브랜치 목록 확인
git branch                 # * feature/greeting

# 4. 원격에 공유
git push -u origin feature/greeting

# 5. main으로 돌아가기
git switch main
ls                         # greeting.txt 없음 (다른 세계)

# 6. (병합은 다음 장에서) 작업 끝난 브랜치 삭제
# git branch -d feature/greeting

6.13 이 장에서 배운 것 (요약)

  • 브랜치 = 특정 커밋을 가리키는 가벼운 포인터 (코드 복사가 아님)
  • git branch(생성), git switch/checkout(이동), -c/-b(생성+이동)
  • 브랜치에서 커밋하면 그 브랜치만 전진, main은 영향 없음
  • HEAD는 현재 브랜치를 가리키는 포인터, HEAD~1은 이전 커밋
  • 전환 전 워킹 디렉터리를 정리(커밋 또는 스태시)
  • git push -u로 원격에 올리고 업스트림 연결
  • git branch -d(안전) / -D(강제) / 원격은 push origin --delete

✍️ 확인 문제

  1. 브랜치를 만들면 코드 전체가 복사되나요? 브랜치의 실제 정체는?
  2. git switchgit restore로 역할이 나뉜 이유는?
  3. git branch -d-D의 차이는?
다음 장에서는 작업 도중 급한 일이 생겼을 때 코드를 잠시 치워두는 스태시(Stash)를 배웁니다. → 07_스태시.md