Git worktree 완벽 가이드
feature 브랜치에서 작업하다가 main에 급한 버그가 터지면 stash, checkout, 수정, 다시 checkout, stash pop. 이 다섯 단계를 매번 반복하고 있다면 git worktree를 써 볼 때입니다. 저장소 하나를 여러 디렉토리에 동시에 체크아웃하는 기능이고, Git 2.5부터 기본으로 들어 있습니다.
매일 겪는 그 불편함
상황을 하나 그려 봅니다. feature 브랜치에서 한창 코드를 고치고 있는데 main에서 긴급 버그가 올라옵니다. 지금까지의 해결법은 둘 중 하나였습니다.
- stash 하고 브랜치를 바꾼다. stash, 전환, 수정, 복귀, stash pop. 단계마다 실수할 곳이 있고, 하던 생각의 흐름이 끊깁니다.
- 프로젝트를 한 번 더 clone 한다. 디스크는 두 배로 쓰고, 두 저장소를 fetch로 맞춰 주는 일이 하나 더 생깁니다.
git worktree는 이 문제를 근본에서 해결합니다. 브랜치를 전환하는 대신 작업 디렉토리를 하나 더 만듭니다.
-git stash -git checkout main -git checkout -b hotfix/critical-bug -# ...수정하고 커밋... -git checkout feature/new-api -git stash pop +git worktree add ../my-project-hotfix -b hotfix/critical-bug main +cd ../my-project-hotfix +# feature 브랜치는 그대로. 작업 디렉토리만 하나 더 생겼다.
git worktree란
git worktree는 하나의 Git 저장소를 여러 작업 디렉토리에 동시에 체크아웃하는 기능입니다. Git 2.5(2015년)부터 정식으로 지원하고, 별도 플러그인이 필요 없습니다.
| 용어 | 의미 |
|---|---|
| Main worktree | git clone이나 git init으로 만들어진 최초의 작업 디렉토리 |
| Linked worktree | git worktree add로 추가한, 연결된 작업 디렉토리 |
.git 디렉토리 | 저장소의 실제 데이터가 있는 곳. linked worktree는 이것을 공유한다 |
.git은 main worktree를 가리키는 포인터입니다.my-project/ ← main worktree (main 브랜치) ├── .git/ ← 실제 저장소 데이터 (공유됨) │ └── worktrees/ ← linked worktree 메타데이터 ├── src/ └── ... ../my-project-hotfix/ ← linked worktree (hotfix 브랜치) ├── .git ← 디렉토리가 아니라 파일 하나 (포인터) ├── src/ └── ... ../my-project-feature/ ← linked worktree (feature 브랜치) ├── .git ← 디렉토리가 아니라 파일 하나 (포인터) ├── src/ └── ...
linked worktree의 .git은 디렉토리가 아니라 파일 하나입니다. 열어 보면 한 줄뿐입니다.
gitdir: /path/to/my-project/.git/worktrees/my-project-hotfix
main worktree의 .git 디렉토리를 가리키는 포인터라서 Git 객체는 중복 없이 공유됩니다. 한쪽에서 fetch한 원격 브랜치를 다른 worktree에서 바로 쓸 수 있는 이유입니다.
기존 방법과 비교
| 방법 | 브랜치 전환 | 디스크 사용 | 동시 작업 | 히스토리 공유 |
|---|---|---|---|---|
브랜치 전환 (checkout) | 매번 | 낮음 | 불가 | 공유 |
| 프로젝트 재clone | 필요 없음 | 매우 높음 | 가능 | 분리 |
| git worktree | 필요 없음 | 낮음 | 가능 | 공유 |
기본 사용법
추가하기
# 기본 문법 git worktree add <경로> [<브랜치>] # 예시 1: 기존 브랜치를 새 디렉토리에 체크아웃 git worktree add ../my-project-hotfix hotfix/login-bug # 예시 2: 새 브랜치를 만들면서 추가 (-b) git worktree add -b feature/new-api ../my-project-feature main # 예시 3: 특정 커밋으로 체크아웃 (detached HEAD) git worktree add ../my-project-review abc1234
목록 보기
git worktree list
~/projects/my-project abc1234 [main] ~/projects/my-project-hotfix def5678 [hotfix/login-bug] ~/projects/my-project-feature ghi9012 [feature/new-api]
스크립트에서 파싱할 때는 --porcelain을 붙입니다. 한 줄에 한 항목, 필드가 고정된 형태로 나옵니다.
git worktree list --porcelain
제거하기
# 권장: git 명령으로 제거 (메타데이터도 같이 정리된다) git worktree remove ../my-project-hotfix # 수정된 파일이 있어도 강제 삭제 git worktree remove --force ../my-project-hotfix # 디렉토리를 손으로 지웠다면, 남은 메타데이터 정리 git worktree prune
rm -rf로 디렉토리를 직접 지웠다면 반드시 git worktree prune을 실행해서 .git/worktrees/ 안에 남은 고아 메타데이터를 청소해야 합니다. 안 하면 그 브랜치가 계속 "체크아웃 중"으로 잡혀 있습니다.
잠그기
외장 드라이브처럼 마운트가 일시적으로 풀릴 수 있는 경로에 worktree가 있다면, prune이 그것을 사라진 worktree로 오해하지 않도록 잠가 둡니다.
git worktree lock ../my-project-hotfix --reason "외장 드라이브에 위치" git worktree unlock ../my-project-hotfix
실전 시나리오
긴급 핫픽스 대응
# feature 브랜치에서 작업 중. stash 없이 바로 핫픽스 디렉토리를 만든다 git worktree add ../my-project-hotfix -b hotfix/critical-bug main # 새 터미널 탭이나 새 에디터 창에서 핫픽스 디렉토리를 연다 cd ../my-project-hotfix # 버그 수정, 커밋, 푸시 vim src/auth/login.ts git add . git commit -m "fix: 로그인 토큰 만료 처리 누락 수정" git push origin hotfix/critical-bug # 핫픽스가 끝나면 worktree 제거 cd ../my-project git worktree remove ../my-project-hotfix
이 과정에서 feature 브랜치의 작업은 파일 하나 건드리지 않습니다.
코드 리뷰와 동시 개발
# PR 리뷰용 worktree git worktree add ../my-project-review feature/teammate-pr cd ../my-project-review npm run dev # 3001 포트에서 리뷰 대상 실행 # 동시에 main worktree에서 내 작업 계속 cd ../my-project npm run dev # 3000 포트에서 내 작업 실행
dev 서버 두 개를 동시에 띄워 놓고 직접 비교하면서 리뷰할 수 있습니다.
장기 릴리즈 브랜치 관리
# v1.x 유지보수 브랜치를 상시 열어 둔다 git worktree add ../my-project-v1 release/v1.x # 필요할 때 언제든 패치 cd ../my-project-v1 git cherry-pick <커밋해시> # main의 버그픽스를 v1에 적용
빌드와 배포 파이프라인
git worktree add ../my-project-staging staging git worktree add ../my-project-prod main # 병렬 빌드 (cd ../my-project-staging && npm run build) & (cd ../my-project-prod && npm run build) & wait
팀에 도입하기
디렉토리 컨벤션 정하기
첫 번째 패턴은 프로젝트명 뒤에 용도를 붙이는 방식입니다. 가장 단순하고, 지금 당장 시작할 수 있습니다.
~/projects/ ├── my-project/ ← main worktree ├── my-project-hotfix/ ← 핫픽스용 ├── my-project-review/ ← PR 리뷰용 └── my-project-exp/ ← 실험용
두 번째 패턴은 프로젝트 폴더 안에 worktree를 모으는 방식입니다. bare 저장소를 써야 하고, 아래에서 따로 다룹니다.
~/projects/my-project/ ├── .bare/ ← bare 저장소 (실제 Git 데이터) ├── main/ ← main worktree ├── hotfix/ ← hotfix worktree └── feature-x/ ← feature worktree
.gitignore
worktree 디렉토리 자체는 Git 추적 대상이 아니므로 .gitignore에 넣을 것이 없습니다.
.env 파일
각 worktree는 독립된 파일 시스템 위에 있습니다. .env가 main worktree에만 있다면 linked worktree에는 없습니다.
# 방법 1: 심볼릭 링크 ln -s ../my-project/.env ../my-project-hotfix/.env # 방법 2: 공유 경로 참조 (.env.shared 같은 파일을 따로 관리) # 방법 3: worktree마다 별도 .env 생성
node_modules
node_modules도 worktree마다 따로 존재합니다.
# 해결책 1: 심볼릭 링크 (패키지 버전이 같을 때만) ln -s ../my-project/node_modules ../my-project-hotfix/node_modules # 해결책 2: pnpm (하드링크 기반 글로벌 캐시로 디스크 중복을 크게 줄인다) # 해결책 3: 그냥 각각 npm install # 브랜치마다 패키지 버전이 다를 수 있으니 이게 가장 안전하다
심화: bare 저장소 방식
일반적인 git clone 대신 bare 저장소로 시작하면 worktree들이 프로젝트 폴더 하나 안에 깔끔하게 모입니다.
# 1. 프로젝트 폴더 생성 mkdir my-project && cd my-project # 2. bare clone (--bare는 작업 파일 없이 .git 데이터만 받는다) git clone --bare git@github.com:my-org/my-project.git .bare # 3. .git 파일 생성 (일반 git 명령이 bare 저장소를 인식하게) echo "gitdir: ./.bare" > .git # 4. 원격 브랜치 fetch 설정 (bare clone은 기본 fetch 설정이 없다) git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" git fetch # 5. 이제 worktree 추가 git worktree add main main git worktree add feature feature/new-api
my-project/ ├── .bare/ ← 실제 Git 데이터 ├── .git ← "gitdir: ./.bare" 한 줄짜리 파일 ├── main/ ← main 브랜치 worktree └── feature/ ← feature 브랜치 worktree
bare 방식이 좋은 이유는 세 가지입니다.
- 명확한 구조. 프로젝트 디렉토리 안에 모든 worktree가 모여 있어 관리가 쉽습니다.
- IDE 친화적. VS Code, IntelliJ에서 프로젝트 루트를 하나로 잡을 수 있습니다.
- 스크립트화 쉬움. 경로가 예측 가능합니다.
팁과 주의사항
쉘 함수로 자동화
.zshrc나 .bashrc에 넣어 둡니다. 브랜치 이름의 /를 -로 바꿔 디렉토리 이름을 만듭니다.
function gwt() {
local branch=$1
local path="../$(basename $(pwd))-${branch//\//-}"
git worktree add "$path" "$branch"
echo "worktree created at: $path"
echo " cd $path"
}
# 사용법
gwt hotfix/login-bug
# → ../my-project-hotfix-login-bug 에 worktree 생성
fzf로 빠르게 이동
function gwtcd() {
local worktree
worktree=$(git worktree list | fzf | awk '{print $1}')
[ -n "$worktree" ] && cd "$worktree"
}
VS Code에서 여러 worktree 열기
# 각 worktree를 새 창으로 code ../my-project-hotfix # 현재 창에 폴더로 추가 (멀티루트 워크스페이스) # File > Add Folder to Workspace
주의할 것 네 가지
- 같은 브랜치는 중복 체크아웃이 안 됩니다.
git worktree add ../another-main main은fatal: 'main' is already checked out으로 실패합니다. - stash 목록은 공유되지만 pop은 현재 worktree에 적용됩니다. stash는
.git/refs/stash에 저장되므로 어느 worktree에서든git stash list로 보입니다. 단,git stash pop은 지금 있는 worktree의 파일에 풀립니다. - git hooks는 공유됩니다.
.git/hooks/가 하나이므로 pre-commit, pre-push 같은 훅이 모든 worktree에 똑같이 적용됩니다. - 서브모듈은 worktree마다 초기화해야 합니다. 새 worktree에서
git submodule update --init --recursive를 한 번 실행하세요.
같이 쓰면 좋은 도구
tmux
worktree마다 창을 하나씩 두면 한 화면에서 네 브랜치를 동시에 봅니다.
┌─────────────────────┬─────────────────────┐ │ my-project (main) │ my-project-hotfix │ │ $ npm run dev │ $ git log --oneline │ ├─────────────────────┼─────────────────────┤ │ my-project-feature │ my-project-review │ │ $ vim src/api.ts │ $ npm test │ └─────────────────────┴─────────────────────┘
lazygit
lazygit은 git worktree list를 UI로 보여 주고 worktree 사이를 쉽게 오가는 TUI 클라이언트입니다.
direnv
worktree 디렉토리마다 다른 환경변수를 자동으로 설정할 수 있습니다.
export NODE_ENV=production export PORT=3001
자주 묻는 질문
worktree는 몇 개까지 만들 수 있나요
기술적인 제한은 없습니다. 다만 worktree마다 에디터, dev 서버, 빌드 프로세스가 메모리를 쓰므로 실용적으로는 2개에서 5개 정도가 적당합니다.
다른 머신에서도 쓸 수 있나요
worktree 메타데이터는 .git/worktrees/에 절대 경로로 저장되므로 다른 머신에서 그대로 쓸 수는 없습니다. 머신마다 git worktree add를 따로 실행해야 합니다.
detached HEAD 상태의 worktree는 어떻게 관리하나요
특정 커밋이나 태그를 체크아웃하면 detached HEAD가 됩니다. 이후 변경을 남기려면 git checkout -b <새브랜치명>으로 브랜치를 만들어야 합니다.
submodule과는 다른 건가요
완전히 다른 개념입니다. submodule은 다른 저장소를 현재 저장소 안에 포함시키는 것이고, worktree는 같은 저장소를 여러 경로에서 동시에 작업하게 하는 것입니다.
마치며
git worktree에는 분명 러닝 커브가 있습니다. 처음엔 "그냥 stash 쓰면 되지 않나" 싶습니다. 하지만 한 번 손에 익으면 브랜치 전환 때문에 치르던 컨텍스트 스위칭 비용이 얼마나 컸는지 실감하게 됩니다. 특히 이런 상황에서 효과가 큽니다.
- 핫픽스가 잦은 서비스. stash 없이 즉시 대응합니다.
- 빌드가 오래 걸리는 프로젝트. 다른 worktree에서 빌드하는 동안 지금 작업을 계속합니다.
- 여러 버전을 동시에 유지보수할 때. v1, v2, main을 한 저장소에서 병렬로 관리합니다.
- 리뷰 중에도 내 작업을 계속해야 할 때. 두 브랜치를 동시에 실행합니다.
도입 순서를 제안하면 이렇습니다.
git worktree add와git worktree remove만 먼저 익힙니다.- 핫픽스 상황에서 실제로 한 번 써 봅니다.
- bare 저장소 방식으로 전환을 고려합니다.
- 팀과 디렉토리 컨벤션을 맞춥니다.
지금 터미널을 열고 한 줄만 쳐 보세요.
git worktree add ../$(basename $(pwd))-hotfix -b hotfix/my-first-worktree main