
업무를 하다 보면 동시에 여러 브랜치에서 작업을 진행하고 싶을 때가 있다.
예를 들어, branch-a에서 A라는 기능을 개발하기 위해 Claude Code를 돌리고 있는데 중간에 branch-b에서 B라는 기능에 대한 버그 수정이 필요할 때가 있다. 그러면 보통 다음과 같은 순서로 처리하게 된다.
branch-a에서 작업하던 내역 git stashbranch-b로 전환node_modules 의존성이 달라져서 재설치이때 git의 worktree라는 개념을 활용하면 작업 내역을 stash하고 다른 브랜치로 체크아웃 하지 않고도 동시에 작업을 진행할 수 있다.
git worktree는 하나의 저장소를 공유하면서 여러 브랜치를 각각 별도 폴더에 동시에 체크아웃해두는 기능이다. 2015년 Git 2.5에 도입된 기능이니 새로운 것은 아니지만, 최근 AI 코딩 에이전트가 보편화되면서 다시 주목받고 있다.
아래와 같이 서로 다른 워크트리를 만들어서 각각 다른 브랜치로 체크아웃해두면 여러 작업을 동시에 진행할 수 있다.
my-project/ ← main 브랜치 (원본, .git 여기 있음)
my-project-hotfix/ ← hotfix/login 브랜치
my-project-review/ ← 리뷰용 PR 브랜치
이렇게 하면 위와 같은 상황에서 기존에 작업하던 워크트리는 그대로 두고 다른 워크트리를 열어 버그를 수정하면 된다. branch-a의 dev server는 계속 돌아가고 있고 빌드 캐시도 살아 있다. 버그 수정뿐만 아니라 다른 사람이 작성한 코드를 리뷰할 때도 유용하다. 내 작업을 건드리지 않고 옆 폴더에서 남의 PR을 직접 실행해볼 수 있다.
위처럼 봤을 때는 "폴더를 두 개 쓰는 거면 git clone을 두 번 하는 거랑 뭐가 다르지?"라는 의문이 들 수 있다. 결과물은 비슷해 보이지만 결정적인 차이가 있다. worktree는 저장소를 공유한다.
my-project-hotfix에서 커밋하면 my-project에서 git log로 바로 확인할 수 있다.git fetch를 한 번만 하면 모든 worktree에 반영된다..git 오브젝트를 공유하므로 디스크를 아낀다. 히스토리가 긴 저장소일수록 차이가 크다.반면 clone을 두 번 하면 두 저장소는 서로 남남이다. 한쪽에서 만든 브랜치를 다른 쪽에서 쓰려면 원격을 거쳐 push하고 fetch해야 한다. 로컬에서만 굴리는 실험용 브랜치라면 아예 방법이 없다.
worktree로 만든 폴더에서 .git을 열어보면 디렉토리가 아니라 파일이다.
$ cat my-project-hotfix/.git
gitdir: /Users/me/projects/my-project/.git/worktrees/my-project-hotfix
원본 저장소를 가리키는 포인터만 들어 있다. 실제 커밋 오브젝트는 전부 원본 .git에 있고, worktree마다 달라야 하는 정보(HEAD, index 등)만 .git/worktrees/<name>/ 아래에 따로 저장된다.
원본 저장소를 main worktree, git worktree add로 만든 폴더를 linked worktree라고 부른다.
# 기존 브랜치를 새 워크트리에 체크아웃
git worktree add ../my-project-hotfix hotfix/login
# 새 브랜치를 만들면서 동시에
git worktree add -b feature/search ../my-project-search
# 브랜치 이름을 생략하면 경로 이름으로 새 브랜치를 만든다
git worktree add ../my-project-search
# 브랜치 없이 특정 커밋만 확인 (detached HEAD)
git worktree add --detach ../my-project-check v1.2.0
# 목록 확인
git worktree list
# 정리 (폴더 삭제 + 메타데이터 제거)
git worktree remove ../my-project-hotfix
# 폴더를 rm으로 지워버렸다면
git worktree prune
# worktree 폴더 이동
git worktree move ../old-path ../new-path
# 실수로 지우거나 옮겨서 연결이 깨졌을 때 복구
git worktree repair
git worktree add ./hotfix처럼 프로젝트 내부에 만들면 안 된다. 번들러의 파일 감시, tsconfig.json의 include, ESLint, tsc --noEmit이 전부 그 폴더를 스캔한다. 같은 코드가 두 벌 존재하니 중복 타입 에러가 쏟아지고, HMR도 이상하게 동작한다.
권장 배치는 두 가지다.
# 1. 형제 폴더에 두기
~/projects/my-project/
~/projects/my-project-hotfix/
# 2. 전용 디렉토리 만들기
~/projects/my-project/
~/worktrees/my-project/hotfix/
worktree를 자주 만들고 지운다면 두 번째 방식이 프로젝트 폴더를 깔끔하게 유지해준다.
실제로 제일 먼저 마주치는 문제다. dev server를 동시에 두 개 띄우면 3000번 포트를 두고 충돌한다.
Vite와 Next.js 모두 점유된 포트를 감지해 다음 번호로 자동 이동하지만, 자동 할당에 의존하면 곤란한 경우가 있다.
.env의 NEXT_PUBLIC_API_URL 같이 포트가 박혀 있는 값webServer.port특히 Playwright는 reuseExistingServer 설정 때문에 엉뚱한 worktree의 dev server에 붙어버리는 사고가 날 수 있다. 다른 브랜치 코드를 대상으로 테스트가 돌아가면 원인을 찾는 데 한참 걸린다. worktree마다 포트를 명시적으로 고정해두는 편이 안전하다.
# my-project → 3000
# my-project-hotfix → 3001
pnpm dev --port 3001
.gitignore 대상은 복사되지 않기 때문에 node_modules, .env, .env.local을 전부 새로 세팅해야 한다. 그 말은 worktree를 만들 때마다 node_modules가 하나씩 더 생긴다는 뜻이다.
pnpm을 쓰면 글로벌 스토어에서 하드링크를 걸기 때문에 실제 추가 용량이 훨씬 적다. worktree를 자주 쓴다면 pnpm을 사용하는 것이 이점이 될 수 있다.
매번 .env 복사하고 install 돌리기가 번거로우니 스크립트를 하나 만들어두면 편하다.
#!/bin/bash
# wt.sh — 사용법: ./wt.sh hotfix-login hotfix/login
PROJECT=$(basename "$PWD")
TARGET="../${PROJECT}-$1"
git worktree add "$TARGET" "$2" || exit 1
cp .env.local "$TARGET/" 2>/dev/null
cd "$TARGET" && pnpm install
.env 계열은 심볼릭 링크를 걸어두는 방법도 있다. 다만 worktree를 지울 때 원본까지 날아가지 않도록 조심해야 한다.
worktree가 최근 다시 주목받는 가장 큰 이유다.
Claude Code 같은 AI 코딩 에이전트는 작업 디렉토리 전체를 자기 것처럼 사용한다. 한 폴더에서 두 개를 동시에 돌리면 서로의 파일 수정을 덮어쓰고, 브랜치를 마음대로 전환해버린다. 사실상 병렬 작업이 불가능하다.
worktree로 폴더를 분리하면 각 에이전트가 독립된 작업 공간을 갖는다. A 기능 개발과 B 버그 수정을 진짜로 병렬 실행할 수 있고, 서로의 컨텍스트도 섞이지 않는다.
실패해도 폴더째 버리면 된다는 점도 매력적이다. 에이전트가 잘못 작업했으면 되돌릴 커밋을 고민할 필요 없이 통째로 날리고 다시 시작하면 된다.
git worktree remove --force ../my-project-experiment
충돌 방지를 위한 제약이다. 같은 브랜치를 두 곳에서 수정하면 어느 쪽 상태가 맞는지 판단할 수 없기 때문이다.
$ git worktree add ../another main
fatal: 'main' is already checked out at '/Users/me/projects/my-project'
-force로 무시할 수 있지만 권장하지 않는다. 같은 커밋을 보기만 하면 되는 상황이라면 -detach를 쓰는 편이 낫다.
.git/hooks와 .git/config는 모든 worktree가 공유한다. husky를 쓰는 프로젝트라면 한 worktree에서 pnpm install로 hooks를 재설치할 때 다른 worktree에도 영향이 간다. 대부분은 문제가 없지만, 브랜치마다 husky 버전이 다르면 예상 밖의 동작이 나올 수 있다.
worktree마다 다른 설정이 필요하면 git config --worktree로 전용 설정을 걸 수 있다. (extensions.worktreeConfig를 먼저 켜야 한다)
폴더를 직접 지우면 .git/worktrees/ 아래의 메타데이터가 그대로 남아 git worktree list에 유령처럼 표시된다. git worktree remove를 쓰거나, 이미 지웠다면 git worktree prune으로 정리하자.
git worktree는 stash와 브랜치 전환을 폴더 전환으로 바꿔주는 기능이다. 저장소를 공유하므로 clone을 여러 번 하는 것보다 가볍고, 커밋 히스토리도 자연스럽게 이어진다.
다만 node_modules 재설치, 포트 충돌, .env 복사 같은 초기 비용이 있으니 스크립트로 자동화해두는 것을 추천한다.