본문 바로가기

Dev Log

Git Worktree 마스터하기X 알아보기O

728x90

최근에 눈에 띈 블로그들이 있었다:

?? 오 저는 git worktree가 뭔지도 몰랐는데요,, ^_^ 알아보자.

 

Git Worktree란?

한 줄 요약부터 하자면:

같은 저장소(repo)를 여러 개의 폴더에 동시에 체크아웃해서 쓰는 기능.

 

기존에 우리가 git을 쓰는 방식은 대충 이런 느낌이다:

my-project/ 
├── .git/ 
└── src/ ← 여기서 checkout으로 브랜치를 갈아탐

폴더 하나에서 git checkout main, git checkout feature/login 왔다갔다 하면서 작업하는 구조. 브랜치를 바꾸면 같은 폴더의 파일 내용이 싹 바뀐다. IDE는 다시 인덱싱하고, 빌드 캐시는 날아가고, 흐름은 끊긴다. 작업하다가 브랜치를 변경하려면 stash 하거나 커밋을 해야하는 번거로움도 있다.

 

Worktree 방식은 이걸 뒤집는다:

`my-project/ ← main 브랜치 
├── .git/ 
└── src/

my-project-hotfix/ ← hotfix 브랜치 (별도 폴더, 같은 repo) 
└── src/

my-project-review/ ← 동료 PR 리뷰용 
└── src/`

브랜치마다 자기만의 폴더를 가진다. 그런데 .git 내부(커밋 히스토리, 오브젝트 등)는 공유한다. clone을 여러 번 하는 것과는 다르다 — 디스크 공간도 훨씬 절약되고, 내부적으로 같은 저장소이기 때문에 브랜치 간 통신도 자유롭다.

 

기본 사용법

새 worktree 만들기

# 기존 브랜치를 새 폴더에 체크아웃
git worktree add ../hotfix hotfix

# 새 브랜치를 만들면서 폴더도 함께 생성
git worktree add -b feature/new-login ../new-login

목록 확인

git worktree list
# /Users/me/my-project         abc1234 [main]
# /Users/me/my-project-hotfix  def5678 [hotfix]

제거

git worktree remove ../hotfix

# 폴더를 수동으로 지웠다면
git worktree prune

 

브랜치 자체는 남아있으니 브랜치까지 지우려면 별도로:

git branch -D hotfix

 

Git Worktree 워크플로우 좀 더 구체적으로 정리

방법 1: 기존 브랜치만 체크아웃

# git worktree add <새 폴더 경로> <체크아웃할 브랜치>
git worktree add ../myapp-hotfix main

이 경우 main 브랜치를 ../myapp-hotfix 폴더에 그대로 체크아웃. 작업하려면 이후에 새 브랜치를 파는 게 좋음 (아래 5번 참조).

방법 2: 새 브랜치 생성 + 체크아웃 한 방에 ⭐ 추천

# git worktree add -b <새 브랜치명> <새 폴더 경로> <기반 브랜치>
git worktree add -b hotfix ../myapp-hotfix main

main에서 파생된 hotfix 브랜치를 만들면서 동시에 ../myapp-hotfix 폴더에 체크아웃. 한 줄로 끝.

 

전체 플로우

# 1. worktree 생성 (방법 2 추천)
git worktree add -b hotfix ../myapp-hotfix main

# 2. 새 폴더로 이동
cd ../myapp-hotfix

# 3. ★ IDE를 새 창으로 연다 ★
code .        # VS Code
# 또는
idea .        # IntelliJ

# 4. 새 IDE 창에서 확인
#    - 창 제목에 hotfix 경로 표시되는지
#    - 좌하단 브랜치 표시 "hotfix"인지

# 5. 방법 1로 만들었다면 여기서 브랜치 생성
#    (방법 2로 만들었으면 이 단계 스킵)
git checkout -b hotfix

# 6. 수정, 커밋, 푸시, PR
git add .
git commit -m "fix: ..."
git push -u origin hotfix
gh pr create --base main

# 7. 작업 끝나면 정리
cd ../myapp-hotfix/..     # 또는 원래 폴더로
git worktree remove ../myapp-hotfix
git branch -D hotfix

기존 작업 폴더는 건드리지도 않는다. 새 터미널 창에서 다른 폴더 열고 딴 일 하는 것과 똑같다.

 

왜 이걸 써야 하냐

세 블로그가 공통적으로 지적하는 지점은 결국 하나다: 컨텍스트 스위칭 비용을 0에 가깝게 만든다는 것. 각자가 짚는 구체적인 상황이 조금씩 다른데, 그 시나리오들을 모아보자.

🔥 상황 1: 긴급 핫픽스

develop 브랜치에서 큰 리팩토링 중이다. 아직 커밋 못 한 변경사항이 가득. 그런데 prod에 긴급 버그 제보가 들어왔다.

기존 방식:

git stash              # 작업 스태시
git checkout main      # 메인 이동 (파일 싹 바뀜)
git pull
git checkout -b hotfix # 핫픽스 브랜치
# ... 수정, 커밋, PR
git checkout develop   # 돌아옴
git stash pop          # 스태시 복구 (운 좋으면 충돌 없음)

IDE 재인덱싱, 빌드 캐시 무효화, 머릿속 컨텍스트 증발. George가 글에서 한 표현 그대로 "sucked the life out of my flow" 다.

👀 상황 2: PR 리뷰

Bill Mill이 특히 강조하는 시나리오다. 동료 PR을 리뷰하려면 보통 그 브랜치를 체크아웃해서 직접 실행해봐야 하는데, 그 순간 내 작업은 멈춘다.

git worktree add ../myapp-review origin/feature/their-pr
cd ../myapp-review
# 빌드, 실행, 테스트 다 해봄
# 내 원래 폴더는 그대로 내 작업 진행

리뷰 끝나면 git worktree remove ../myapp-review로 청소. 흔적 없이 사라진다.

🧪 상황 3: 오래 걸리는 테스트/빌드와 병행 개발

# 현재 폴더에서 E2E 테스트 30분짜리 돌리고
npm run test:e2e &

# 다른 worktree에서 새 기능 개발
cd ../myapp-feature
# 테스트와 포트, 빌드 캐시 충돌 없이 개발

📌 상황 4: 메인 브랜치를 항상 손에 들고 있기

Bill Mill의 워크플로우 중 인상적이었던 부분. 그는 main 브랜치 전용 폴더를 상시 유지한다.

  • 기능 작업 중에 "원래 main에선 이 부분이 어떻게 돼있었지?" 궁금할 때 → main 폴더에서 바로 grep
  • 동료가 "이 기능 지금 어떻게 동작해?" 물어볼 때 → main 폴더 열어 확인
  • 내 작업 브랜치는 건드리지 않고

git show main:path/to/file 같은 어색한 명령어를 외우지 않아도 된다. 그냥 폴더를 바꾸면 된다.

 

Worktree 주의점: untracked 파일은 안 넘어온다

앞서 소개한 블로그 중 3번째 글의 저자인 Bill Mill이 짚는 worktree의 가장 큰 실무 걸림돌이 나온다.

git worktree add는 git이 추적하는 파일만 체크아웃한다. 즉:

  • node_modules/ ❌
  • .env, .envrc ❌
  • 빌드 산출물 (dist/, build/) ❌
  • .vscode/settings.json (gitignore에 있다면) ❌
  • 로컬 설정 파일들 ❌

Bill Mill은 "회사 레포에선 npm install이 2분이나 걸린다. worktree 만들 때마다 2분씩 기다려야 하면 나도 worktree 안 쓴다"고 솔직하게 말한다.

해결 방법들

1. pnpm 같은 패키지 매니저 사용

pnpm은 글로벌 스토어에 패키지를 두고 각 폴더엔 심볼릭 링크만 건다. 여러 worktree여도 디스크 중복 거의 없음.

2. 직접 복사 스크립트

Bill Mill은 자기만의 worktree 쉘 스크립트를 만들어 쓴다. 이 스크립트는:

  • 브랜치 이름으로 폴더 이름을 자동 생성
  • 새 브랜치 여부 자동 판단 (있으면 체크아웃, 없으면 생성)
  • node_modules, .env 같은 untracked 파일을 copy-on-write로 복제
  • 생성 후 해당 폴더로 자동 cd
. worktree new-feature
# → 폴더 생성, 의존성 복제, cd까지 한 번에

3. 심볼릭 링크

cd ../myapp-feature
ln -s ../myapp/node_modules ./node_modules
cp ../myapp/.env ./

간단하지만 브랜치별로 패키지가 다르면 꼬일 수 있어 주의.

4. Docker / devcontainer

환경 자체를 격리된 컨테이너로 두면 worktree가 더 가벼워진다.

 

실용 세팅 팁

프로젝트 폴더 구조

Bill Mill이 제안하는 구조가 꽤 합리적이다:

my-weather-app/ ← 프로젝트 루트 (그 자체는 repo가 아님) 
├── main/ ← main 브랜치 worktree 
├── update-node-deps/ ← 의존성 업데이트 작업 
├── verify-user-input/ ← 기능 개발 중 
└── hotfix-login-bug/ ← 핫픽스

각 폴더가 독립된 worktree. 루트 폴더는 단순한 컨테이너 역할.

IDE 열기

VS Code나 IntelliJ는 worktree마다 별도 창으로 여는 게 정답이다. 같은 창에서 폴더만 바꾸면 인덱스가 꼬인다.

cd ../myapp-feature code . # 새 VS Code 창

네이밍 컨벤션

터미널에서 식별 쉬우려면:

myapp/ # 메인 myapp.hotfix/ # 핫픽스 (접미사 .) myapp.review-pr-123/ # 리뷰용 myapp.feat-login/ # 기능 작업

 

언제 안 쓰는 게 좋냐

세 블로그 모두 "무조건 쓰라"고 하는데, 내 생각엔 항상 이득은 아니다.

Worktree가 오히려 부담되는 상황:

  • 혼자 하는 작은 프로젝트: 브랜치 간 전환이 드물면 stash-checkout으로도 충분
  • 의존성 설치가 엄청 무거운 프로젝트: 스크립트 없이 git worktree 쓰면 매번 고통
  • 디스크 공간이 빠듯한 환경: 비록 .git은 공유해도 worktree마다 node_modules, 빌드 산출물은 따로 생김
  • 엄격한 모노레포 / Turborepo: 캐시 공유 구조랑 맞물려 복잡도 올라갈 수 있음

그렇지만 일단 몇주간 회사에서 열심히 써보도록 하고 후기를 남기도록 하겠다.

728x90