저장소 하나에서 작업 폴더를 여러 개 꺼내 쓰는 git worktree를 처음부터 끝까지 다룹니다. add·list·remove·prune·lock·move를 git 2.55 실제 출력으로 따라가고, 커밋·브랜치·stash·설정은 공유되지만 파일·스테이징·HEAD는 폴더마다 따로라는 사실을 명령으로 하나씩 증명합니다. 같은 브랜치를 두 곳에서 꺼낼 수 없는 이유, worktree마다 node_modules·포트·.env를 따로 챙겨야 하는 함정, switch·stash·clone과의 비교까지 정리합니다.
민지는 요즘 cafe-menu 주문 웹앱에 메뉴 검색 기능을 붙이고 있습니다. src/order.js를 반쯤 고쳐 놓은 참인데, 사장님에게서 메시지가 옵니다. "카페라테 가격이 오늘부터 5,500원이에요. 지금 바로 바꿔 주세요."
예전의 민지라면 이렇게 했을 겁니다. 고치던 파일을 git stash로 치워 두고, git switch로 가격 수정 브랜치로 갈아타고, 고치고, 커밋하고, 다시 돌아와서 git stash pop. 할 수는 있지만 매번 조마조마합니다. stash를 깜빡하면 파일이 섞이고, 개발 서버는 갈아탈 때마다 다시 빌드됩니다.
게다가 민지에게는 새로운 고민이 생겼습니다. 요즘은 Claude Code에게 검색 기능을 맡기고, 그동안 Codex에게는 결제 버그를 맡기고 싶습니다. 그런데 두 에이전트가 한 폴더에서 동시에 일하면 어떻게 될까요? 한쪽이 git switch를 하는 순간 다른 쪽이 고치던 파일이 통째로 바뀝니다. 한쪽이 git add .을 하면 다른 쪽의 반쯤 고친 파일까지 커밋에 딸려 들어갑니다.
이 두 문제를 한 번에 푸는 도구가 이번 편의 주인공 git worktree(깃 워크트리)입니다. 저장소는 하나인데 작업 폴더를 여러 개 꺼내 놓고, 폴더마다 다른 브랜치를 펼쳐 두는 기능입니다. 1편에서 세운 첫 번째 원칙, 작업 1개 = worktree 1개 = 브랜치 1개 = 에이전트 세션 1개의 바탕이 되는 기능이기도 합니다.
이번 편에서는 에이전트 도구를 아직 쓰지 않습니다. 순수한 git만으로 worktree를 만들고, 들여다보고, 치우는 법을 끝까지 익힙니다. Claude Code의 --worktree나 Worktrunk 같은 도구가 뒤에서 무슨 일을 하는지는, 이 기초를 알면 3편에서 훨씬 쉽게 보입니다.
이 글의 기준 — 터미널 출력은 모두 2026년 9월 기준 git 2.55에서 직접 실행해 얻은 것이고, 영어 출력(LC_ALL=C)으로 통일했습니다. GitHub 대신 내 컴퓨터에 만든 가짜 원격 저장소(git init --bare)로 재현했고, 출력 속 원격 주소는 https://github.com/minji/cafe-menu.git으로, 작업 폴더 경로는 /Users/minji/work로 바꿔 적었습니다. 해시와 출력 구조는 그대로입니다.
1. worktree란: 한 저장소, 여러 작업대
목공방을 떠올려 봅시다. 공방에는 공구 창고가 하나 있습니다. 나무, 설계도 묶음, 지금까지 만든 작품의 기록이 모두 여기에 보관됩니다. 그리고 창고 앞에 작업대가 여러 개 놓여 있습니다. 작업대 A에서는 의자를, 작업대 B에서는 책상을 만듭니다. 작업대마다 올려 둔 나무 조각과 공구는 따로라서, A에서 톱질을 하다 말고 B로 가도 A의 작업물은 그대로 놓여 있습니다. 하지만 완성한 작품을 기록하는 장부는 창고에 한 권뿐이라, 어느 작업대에서 기록해도 같은 장부에 적힙니다.
cafe-menu/.git 커밋 · 브랜치 · stash · 설정 · 원격 · 훅 (하나뿐)
용어 두 개만 짚고 가겠습니다. 처음 git clone이나 git init으로 만든 원래 폴더를 main worktree(메인 워크트리)라고 부릅니다. 여기에 진짜 .git 폴더가 있습니다. git worktree add로 나중에 늘린 폴더는 linked worktree(연결된 워크트리)라고 부릅니다. 이름 그대로 main worktree의 .git에 "연결"되어 있을 뿐, 자기만의 저장소를 갖지 않습니다. 여기서 main은 브랜치 이름 main과는 상관없는 말입니다. main worktree에서 다른 브랜치를 꺼내 두어도 여전히 main worktree입니다.
이것이 clone을 하나 더 하는 것과 결정적으로 다른 점입니다. clone은 창고째 하나 더 짓는 일입니다. 창고가 둘이니 한쪽에서 만든 커밋을 다른 쪽이 보려면 push와 fetch를 거쳐야 합니다. worktree는 창고는 그대로 두고 작업대만 하나 더 놓는 일이라, 한 폴더에서 커밋하는 순간 다른 폴더에서도 그 커밋이 보입니다.
브랜치가 무엇인지 가물가물하다면 「Git, 이것만 알면 협업한다」 4편을 먼저 보고 오세요. 한 줄로 다시 말하면, 브랜치는 커밋에 붙은 "움직이는 이름표"입니다. 지금까지는 한 폴더에서 이 이름표 사이를 git switch로 옮겨 다녔다면, worktree를 쓰면 이름표마다 폴더를 따로 둘 수 있습니다.
민지의 저장소 상태부터 보겠습니다. cafe-menu 폴더에는 index.html, menu.md, src/order.js, package.json, 그리고 .gitignore로 무시하는 .env가 있습니다. 브랜치는 main과, 전에 만들어 둔 fix/latte-price 두 개입니다.
bash
cd ~/work/cafe-menu
git worktree list
text
/Users/minji/work/cafe-menu 2299675 [main]
worktree를 한 번도 만들지 않았어도 목록에는 한 줄이 나옵니다. 원래 폴더도 worktree(main worktree)이기 때문입니다.
2-1. 새 브랜치와 함께 만들기
검색 기능을 따로 떼어 작업할 폴더를 만듭니다. 명령의 모양은 git worktree add <폴더 경로> -b <새 브랜치 이름>입니다.
Preparing worktree (new branch 'feature/search')
HEAD is now at 2299675 주문 웹앱 첫 버전
한 줄로 세 가지 일이 일어났습니다.
1새 브랜치 feature/search를 만듭니다. 기준점은 지금 있는 커밋(2299675)입니다. git branch feature/search를 한 것과 같습니다.
2옆 폴더 ../cafe-menu.search를 만들고 그 브랜치의 파일을 꺼내 놓습니다(체크아웃).
3저장소에 "이 폴더가 worktree다"라고 등록합니다. 그래서 git worktree list에 나타나고, 나중에 remove로 깔끔하게 치울 수 있습니다.
기준점을 지금 커밋이 아닌 다른 곳으로 하고 싶다면 맨 뒤에 붙여 줍니다. 예를 들어 최신 원격 main에서 갈라지고 싶다면 git fetch 후 git worktree add ../cafe-menu.search -b feature/search origin/main처럼 씁니다.
2-2. 이미 있는 브랜치를 꺼내기
라테 가격 수정용 브랜치 fix/latte-price는 이미 있습니다. 이럴 때는 -b 없이 브랜치 이름만 뒤에 적습니다.
From https://github.com/minji/cafe-menu
* [new branch] feature/dessert -> origin/feature/dessert
Preparing worktree (new branch 'feature/dessert')
branch 'feature/dessert' set up to track 'origin/feature/dessert'.
HEAD is now at f8eea9e 디저트 메뉴 초안
로컬에 feature/dessert가 없고 원격에 같은 이름의 브랜치가 딱 하나 있으면, git이 알아서 같은 이름의 로컬 브랜치를 만들고 원격 브랜치를 따라가도록(추적하도록) 연결해 줍니다. git switch feature/dessert가 해 주던 것과 같은 편의 기능입니다.
2-4. 브랜치 이름을 생략하면 생기는 일
-b도 브랜치 이름도 없이 폴더만 적으면 어떻게 될까요?
bash
git worktree add ../cafe-menu.hotfix
text
Preparing worktree (new branch 'cafe-menu.hotfix')
HEAD is now at 2299675 주문 웹앱 첫 버전
git 공식 문서에 따르면 이 경우 폴더 이름의 마지막 부분을 브랜치 이름으로 씁니다.../hotfix라고 썼다면 hotfix 브랜치가 생겼을 텐데, 민지처럼 cafe-menu.hotfix라는 폴더 이름 규칙을 쓰면 브랜치 이름까지 cafe-menu.hotfix가 되어 버립니다. 편하긴 하지만 브랜치 목록이 어지러워지니, -b로 브랜치 이름을 늘 직접 적는 습관을 권합니다.
💡
세 가지 모양만 기억하세요 — 새 브랜치로 시작: git worktree add <폴더> -b <새 브랜치> · 있는 브랜치 꺼내기: git worktree add <폴더> <브랜치> · 브랜치 없이 구경만: git worktree add --detach <폴더> <커밋/브랜치>(6절). 대부분의 상황은 이 셋으로 해결됩니다.
한 줄에 세 가지 정보가 있습니다. 폴더의 전체 경로, 그 폴더가 지금 가리키는 커밋 해시, 대괄호 안의 브랜치 이름입니다. 세 폴더 모두 방금 같은 커밋에서 출발했으니 해시가 같습니다. 이 명령은 main 폴더에서 치든 linked 폴더에서 치든 똑같은 목록을 보여 줍니다. 창고가 하나라서, 어느 작업대에서 물어봐도 작업대 목록은 같습니다.
디스크를 봐도 폴더가 나란히 생겨 있습니다.
bash
ls ~/work
text
cafe-menu
cafe-menu.latte
cafe-menu.search
3-1. 스크립트가 읽기 좋은 --porcelain
사람이 읽는 목록 대신 프로그램이 읽기 좋은 형식이 필요할 때는 --porcelain을 붙입니다. 3편에서 볼 도구들도 내부적으로 이런 정보를 읽어 갑니다.
bash
git worktree list --porcelain
text
worktree /Users/minji/work/cafe-menu
HEAD 2299675217af89617bc58d9639f59daf484ef235
branch refs/heads/main
worktree /Users/minji/work/cafe-menu.latte
HEAD 2299675217af89617bc58d9639f59daf484ef235
branch refs/heads/fix/latte-price
worktree /Users/minji/work/cafe-menu.search
HEAD 2299675217af89617bc58d9639f59daf484ef235
branch refs/heads/feature/search
worktree 하나당 한 덩어리, 덩어리 사이는 빈 줄입니다. 해시는 전체 40자로, 브랜치는 refs/heads/까지 붙은 정식 이름으로 나옵니다. 직접 쓸 일은 드물지만 "worktree 정보는 결국 이 세 줄"이라는 걸 알아 두면 나중에 도구가 이상하게 굴 때 원인을 찾기 쉽습니다.
목록 끝에 locked나 prunable 같은 꼬리표가 붙기도 합니다. 둘 다 뒤에서 직접 만들어 보겠습니다. -v를 붙이면 꼬리표의 이유까지 한 줄 더 보여 줍니다.
그래서 linked worktree는 가볍습니다. 재현에 쓴 작은 저장소에서 main 폴더의 .git은 212KB였지만, cafe-menu.search 폴더 전체는 24KB였습니다. 커밋 기록을 복사하지 않고 파일만 꺼냈기 때문입니다. 기록이 수백 MB인 큰 저장소일수록 clone을 하나 더 하는 것보다 worktree가 훨씬 빠르고 가볍습니다.
⚠️
main 폴더는 창고다 — linked worktree가 남아 있는 상태에서 main 폴더(cafe-menu/)를 통째로 지우면, 창고가 사라진 것이라 모든 linked 폴더가 한꺼번에 git 저장소가 아니게 됩니다. 재현해 보니 linked 폴더에서 git status를 치자 fatal: not a git repository: (null)만 나왔습니다. push하지 않은 커밋은 함께 사라집니다. 작업대를 치울 때는 main 폴더가 아니라 linked 폴더를 치우세요.
5. 같이 쓰는 것과 따로 쓰는 것: 명령으로 증명하기
worktree를 쓰기 전에 꼭 머리에 넣어야 할 표가 있습니다. 무엇이 모든 폴더에 공유되고, 무엇이 폴더마다 따로인가. 말로 외우기보다 하나씩 직접 확인해 보겠습니다.
5-1. 커밋 기록: 공유 — push 없이 바로 보인다
cafe-menu.search에서 검색 함수를 추가하고 커밋합니다.
bash
cd ~/work/cafe-menu.search
echo'export function search(q){ return q.trim(); }' >> src/order.js
git add src/order.js && git commit -m "메뉴 검색 함수 추가"
text
[feature/search ab5de5e] 메뉴 검색 함수 추가
1 file changed, 1 insertion(+)
이제 main 폴더로 가서 전체 기록을 봅니다. push도 fetch도 하지 않았습니다.
bash
cd ~/work/cafe-menu
git log --oneline --all
text
ab5de5e 메뉴 검색 함수 추가
2299675 주문 웹앱 첫 버전
방금 옆 폴더에서 만든 커밋 ab5de5e가 그대로 보입니다. 커밋은 공용 창고(.git/objects)에 저장되기 때문입니다. main 폴더에서 git show feature/search로 내용을 보거나, git merge feature/search로 바로 합칠 수도 있습니다.
5-2. 작업 파일: 따로 — 옆 폴더의 수정은 내 파일에 없다
그런데 main 폴더의 src/order.js를 열어 보면 검색 함수가 없습니다.
bash
cat src/order.js
text
export function order(item){ return item; }
당연합니다. main 폴더는 여전히 main 브랜치를 펼쳐 두고 있고, 검색 함수 커밋은 feature/search 브랜치에만 있으니까요. 기록은 공유되지만, 펼쳐 놓은 파일은 폴더마다 자기 브랜치의 것입니다. 바로 이 성질 덕분에 두 에이전트가 두 폴더에서 동시에 파일을 고쳐도 서로 덮어쓰지 않습니다.
5-3. 브랜치 목록: 공유 — 다른 폴더가 쓰는 브랜치엔 +
bash
git branch
text
+ feature/search
+ fix/latte-price
* main
브랜치 이름표도 공용 창고에 있으니 어느 폴더에서나 같은 목록이 나옵니다. 눈여겨볼 것은 앞의 기호입니다. *는 "지금 이 폴더가 쓰는 브랜치", +는 "다른 worktree가 쓰고 있는 브랜치"입니다. 이 +가 6절의 중요한 규칙으로 이어집니다.
5-4. 스테이징과 HEAD: 따로
cafe-menu.latte에서 가격을 고치고 git add까지 해 둡니다.
bash
cd ~/work/cafe-menu.latte
sed -i '''s/카페라테 5000/카페라테 5500/' menu.md
git add menu.md
git status --short
text
M menu.md
같은 순간 main 폴더의 상태를 보면 아무것도 없습니다.
bash
cd ~/work/cafe-menu
git status --short
text
위 출력 칸이 비어 있는 것이 실제 결과입니다. 스테이징 영역(index)은 앞에서 본 개인 서랍 안의 index 파일이라 폴더마다 따로입니다. 한 폴더에서 git add .을 해도 다른 폴더의 변경은 딸려 오지 않습니다. 지금 브랜치(HEAD)도 마찬가지로 폴더마다 따로 기록됩니다. main 폴더에서 git rev-parse --abbrev-ref HEAD를 치면 main이, latte 폴더에서 치면 fix/latte-price가 나옵니다.
5-5. stash: 공유 — 편리하지만 조심
latte 폴더에서 방금 고친 것을 stash로 잠깐 치워 봅니다.
bash
cd ~/work/cafe-menu.latte
git stash push -m "라테 가격 고민 중"
text
Saved working directory and index state On fix/latte-price: 라테 가격 고민 중
그리고 search 폴더에서 stash 목록을 봅니다.
bash
cd ~/work/cafe-menu.search
git stash list
text
stash@{0}: On fix/latte-price: 라테 가격 고민 중
다른 폴더에서 만든 stash가 보입니다. stash는 refs/stash라는 공용 이름표에 쌓이기 때문입니다. 편리할 때도 있지만(한 폴더에서 치운 것을 다른 폴더에서 꺼낼 수 있음), 엉뚱한 폴더에서 무심코 git stash pop을 하면 다른 작업의 변경이 이 폴더에 풀립니다. worktree를 쓰기 시작하면 stash는 거의 쓸 일이 없어지지만, 쓴다면 메시지의 On fix/latte-price 부분을 꼭 확인하세요. 원래 폴더로 돌아가 git stash pop으로 되살리고 커밋해 둡니다.
bash
cd ~/work/cafe-menu.latte
git stash pop
git commit -am "카페라테 가격 5500원으로"
5-6. 설정·원격·훅: 공유
main 폴더에서 저장소 설정을 하나 바꾸고, search 폴더에서 읽어 봅니다.
bash
cd ~/work/cafe-menu
git config set pull.rebase truecd ~/work/cafe-menu.search
git config get pull.rebase
git remote -v
.git/config는 하나뿐이니 설정도 원격 주소도 공유됩니다. 훅(hook, 커밋 같은 순간에 자동으로 도는 스크립트)도 마찬가지입니다. main 폴더의 .git/hooks/pre-commit에 "훅 실행" 메시지를 찍는 스크립트를 넣고 search 폴더에서 커밋해 봤습니다.
bash
git commit -am "검색 TODO 메모"
text
[pre-commit] 공용 훅 실행: /Users/minji/work/cafe-menu.search
[feature/search 3a0a784] 검색 TODO 메모
1 file changed, 1 insertion(+)
훅은 공용 창고의 것이 돌지만, 도는 위치(현재 폴더)는 커밋한 worktree입니다. git rev-parse --git-path hooks를 치면 search 폴더에서도 /Users/minji/work/cafe-menu/.git/hooks가 나옵니다. 설정을 폴더마다 다르게 두는 방법(extensions.worktreeConfig와 git config --worktree)도 있지만, 입문 단계에서는 "설정은 하나"라고 기억해 두면 충분합니다.
5-7. .env와 node_modules: 따라오지 않는다
마지막으로, 새 worktree에 없는 것입니다. 4절의 ls -a 결과를 다시 보면 .env가 없었습니다.
bash
ls ../cafe-menu/.env ../cafe-menu.search/.env
text
ls: ../cafe-menu.search/.env: No such file or directory
../cafe-menu/.env
worktree는 커밋된 파일만 꺼내 놓습니다. .gitignore로 무시한 파일(.env, node_modules/, 빌드 결과물)은 커밋에 없으니 새 폴더에 따라오지 않습니다. 이것은 버그가 아니라 설계입니다. 비밀번호나 설치물은 저장소에 넣지 않는다는 원칙(1편)을 지킨 결과입니다. 다만 실제로 일하려면 챙겨야 하니, 10절에서 대처법을 봅니다.
아래 위젯에서 항목을 눌러 판정과 증명 명령을 다시 확인해 보세요.
모든 worktree가 공유
폴더마다 따로
따라오지 않음
커밋 기록(objects)
작업 파일
.env 같은 무시된 파일
브랜치·태그 이름표
스테이징(index)
node_modules/, 빌드 결과
stash
HEAD(지금 브랜치)
개발 서버·포트(운영체제 자원)
설정·원격·훅
진행 중인 merge·rebase 상태
IDE 창, 터미널 세션
표의 "진행 중인 merge·rebase 상태"는 한 폴더에서 rebase 충돌을 푸는 중이어도 다른 폴더는 영향을 받지 않는다는 뜻입니다. rebase나 merge 도중 상태 파일이 개인 서랍에 저장되기 때문입니다. 공식 문서는 브랜치 이름표 가운데 refs/bisect, refs/worktree, refs/rewritten만 예외로 폴더마다 따로 둔다고 적고 있습니다.
6. 같은 브랜치는 한 곳에서만: "is already used by worktree"
search 폴더에서 main 브랜치로 잠깐 갈아타 보려 합니다.
bash
cd ~/work/cafe-menu.search
git switch main
text
fatal: 'main' is already used by worktree at '/Users/minji/work/cafe-menu'
거절당했습니다. 한 브랜치는 한 번에 한 worktree에서만 꺼낼 수 있습니다. 라테 브랜치도 마찬가지이고, 새 worktree를 만들 때도 같은 규칙이 적용됩니다.
bash
git switch fix/latte-price
cd ~/work/cafe-menu
git worktree add ../cafe-menu.search2 feature/search
text
fatal: 'fix/latte-price' is already used by worktree at '/Users/minji/work/cafe-menu.latte'
Preparing worktree (checking out 'feature/search')
fatal: 'feature/search' is already used by worktree at '/Users/minji/work/cafe-menu.search'
처음 보면 불편한 제약 같지만, 이유를 알면 고마운 안전장치입니다. 두 폴더가 같은 브랜치를 펼쳐 두었다고 상상해 봅시다. A 폴더에서 커밋하면 브랜치 이름표가 새 커밋으로 움직입니다. 그런데 B 폴더의 파일과 스테이징은 여전히 옛 커밋 기준입니다. B 폴더에서 아무 생각 없이 커밋하면 A의 변경을 되돌리는 커밋이 만들어지거나, git status가 이해할 수 없는 차이를 쏟아 냅니다. git은 이 혼란을 아예 막아 버리는 쪽을 택했습니다.
에이전트에게는 이 규칙이 특히 중요합니다. "같은 브랜치를 두 에이전트가 동시에 만지는 일"이 구조적으로 불가능해지기 때문입니다. 에이전트 둘에게 같은 기능을 맡기고 싶다면 브랜치를 둘로 나눠야 하고(agent/search-claude, agent/search-codex), 좋은 쪽을 고르는 법은 5편에서 다룹니다.
🚫
"main을 이 폴더에서도 보고 싶은데요"
main 브랜치는 main 폴더가 쓰고 있으니 다른 폴더에서 git switch main은 안 됩니다. 억지로 --force(--ignore-other-worktrees)를 쓰는 방법도 있지만 위에서 말한 혼란을 스스로 부르는 일입니다.
🔍
보기만 할 거라면 브랜치 없이 꺼낸다
--detach로 만들면 브랜치 이름표를 쥐지 않고 커밋만 펼쳐 둡니다. 그래서 다른 폴더가 쓰는 브랜치라도 얼마든지 꺼내 볼 수 있습니다. 파일 내용만 보고 싶다면 폴더를 늘리지 않고 git show main:menu.md도 됩니다.
✅
고칠 거라면 새 브랜치를 판다
변경을 할 거라면 그 브랜치에서 새 브랜치를 갈라 따로 작업하고, 나중에 merge합니다. 이것이 worktree 시대의 기본자세입니다.
6-1. --detach: 브랜치 없이 구경용 작업대
도윤에게 검색 기능을 보여 주려고, 리뷰용 폴더를 하나 더 만들어 봅니다. feature/search는 이미 search 폴더가 쓰고 있지만 --detach면 괜찮습니다.
bash
git worktree add --detach ../cafe-menu.review feature/search
git worktree list
text
Preparing worktree (detached HEAD 3a0a784)
HEAD is now at 3a0a784 검색 TODO 메모
/Users/minji/work/cafe-menu 2299675 [main]
/Users/minji/work/cafe-menu.latte 5a02022 [fix/latte-price]
/Users/minji/work/cafe-menu.review 3a0a784 (detached HEAD)
/Users/minji/work/cafe-menu.search 3a0a784 [feature/search]
review 폴더는 대괄호 대신 (detached HEAD)로 표시됩니다. 브랜치 이름표 없이 커밋 3a0a784 위에 서 있다는 뜻입니다. detached HEAD가 낯설다면 「Git, 이것만 알면 협업한다」 8편의 11절을 참고하세요. 여기서 커밋해도 되지만 어느 브랜치에도 속하지 않으니, 남기고 싶은 커밋이 생기면 git switch -c 새-브랜치로 이름표를 붙여 주세요. 리뷰나 테스트, 빌드 확인처럼 보고 버릴 폴더에 딱 맞는 방식입니다.
6-2. worktree가 쓰는 브랜치는 지울 수도 없다
같은 규칙이 브랜치 삭제에도 적용됩니다. 누가 쓰고 있는 브랜치는 -D(강제 삭제)로도 지워지지 않습니다.
error: cannot delete branch 'feature/search' used by worktree at '/Users/minji/work/cafe-menu.search'
error: cannot delete branch 'feature/search' used by worktree at '/Users/minji/work/cafe-menu.search'
그래서 정리 순서는 항상 worktree를 먼저 치우고, 그다음 브랜치를 지운다입니다. 다음 절에서 이 순서를 따라가 봅니다.
리뷰를 마친 review 폴더를 치우려는데, 리뷰하며 적어 둔 메모 파일 notes.txt가 남아 있습니다.
bash
git worktree remove ../cafe-menu.review
text
fatal: '../cafe-menu.review' contains modified or untracked files, use --force to delete it
git이 한 번 막아 줍니다. 커밋하지 않은 수정이나 새 파일이 있는 폴더는 지우지 않습니다. 지우면 되살릴 방법이 없기 때문입니다. 메모가 필요 없다는 걸 확인했다면 --force로 지웁니다.
bash
git worktree remove --force ../cafe-menu.review
--force에는 출력이 없습니다. 조용히 폴더와 개인 서랍을 함께 지웁니다. 에이전트가 이 --force를 스스로 붙이지 않게 하는 것이 3편의 정리 규칙 가운데 하나입니다.
재미있는 점은 .gitignore로 무시된 파일은 이 검사에 걸리지 않는다는 것입니다. node_modules/만 잔뜩 들어 있는 폴더는 --force 없이도 그냥 지워졌습니다(재현해서 확인). 무시된 파일은 "다시 만들 수 있는 것"으로 보기 때문입니다. 반대로 말하면, 무시된 .env에 직접 적어 둔 값이 있다면 remove가 경고 없이 함께 지운다는 뜻이니 주의하세요.
7-2. 브랜치는 남는다
수정 없이 깨끗한 latte 폴더를 치우고 브랜치 목록을 봅니다.
bash
git worktree remove ../cafe-menu.latte
git branch
text
+ feature/search
fix/latte-price
* main
fix/latte-price 앞의 +가 사라졌습니다. 이제 아무 폴더도 이 브랜치를 쓰지 않는다는 뜻입니다. 그리고 브랜치 자체와 커밋(5a02022 카페라테 가격 5500원으로)은 그대로 남아 있습니다. worktree remove는 작업대만 치우지 장부를 찢지 않습니다. 커밋해 둔 작업은 폴더를 지워도 안전하다는 것, 이것이 "폴더를 지우기 전에 커밋하라"는 규칙의 이유입니다.
7-3. 다 끝난 작업의 정리 순서
검색 기능이 끝났다면 정리는 네 단계입니다. main 폴더에서 합치고, 작업대를 치우고, 브랜치를 지웁니다. (아래 명령은 실제로는 이 글의 실습을 모두 마친 뒤 맨 마지막에 실행했습니다. 그래서 7-4절부터는 search 폴더가 아직 남아 있는 상태로 이어집니다.)
main 폴더에서 feature/search를 merge할 수 있었다는 점에 주목하세요. 그 브랜치를 search 폴더가 쓰고 있어도 합치는 데는 문제가 없습니다. 막히는 것은 "꺼내기(checkout)"와 "지우기"뿐입니다. 실제 팀 작업이라면 merge 대신 push하고 Pull Request를 거치겠지만(「Git, 이것만 알면 협업한다」 6편), 정리 순서는 같습니다.
1작업 폴더에서 커밋하고 push — 폴더를 지워도 기록이 남도록.
2PR로 합치기(혼자라면 main 폴더에서 git merge).
3git worktree remove <폴더> — 거절되면 무엇이 남았는지 먼저 확인.
4git branch -d <브랜치> — 합쳐진 브랜치만 지워지므로 안전.
7-4. 폴더를 손으로 지웠다면: git worktree prune
Finder에서 폴더를 휴지통에 버리거나 rm -rf로 지우면 어떻게 될까요? 실험용 worktree를 만들고 손으로 지워 봅니다.
폴더는 없어졌지만 git은 아직 기억하고 있습니다. 개인 서랍(.git/worktrees/cafe-menu.old)이 남아 있기 때문입니다. 대신 prunable(정리 가능)이라는 꼬리표가 붙었습니다. 이 상태에서는 불편한 일이 생깁니다. 서랍이 여전히 experiment/old를 쥐고 있어서, 다른 곳에서 이 브랜치를 꺼내거나 지울 수 없습니다.
Preparing worktree (checking out 'experiment/old')
fatal: 'experiment/old' is already used by worktree at '/Users/minji/work/cafe-menu.old'
error: cannot delete branch 'experiment/old' used by worktree at '/Users/minji/work/cafe-menu.old'
같은 경로로 다시 만들려 해도 막힙니다.
text
fatal: '../cafe-menu.old' is a missing but already registered worktree;
use 'add -f' to override, or 'prune' or 'remove' to clear
이럴 때 쓰는 것이 prune(가지치기)입니다. --dry-run(-n)으로 무엇을 정리할지 먼저 보고, -v로 무엇을 했는지 보며 실행합니다.
"안내문이 가리키는 폴더가 없으니 서랍을 치웠다"는 메시지입니다. 이번에도 브랜치 experiment/old는 남아 있으니, 필요 없으면 git branch -D experiment/old로 따로 지웁니다. 폴더가 없어진 worktree는 git worktree remove ../cafe-menu.old로 치워도 됩니다(출력 없이 등록만 지웁니다).
prune을 잊어도 큰일은 나지 않습니다. 공식 문서에 따르면 git gc가 돌 때 git worktree prune --expire 3.months.ago가 함께 실행되어, 석 달 넘게 폴더가 없는 서랍은 자동으로 정리됩니다(gc.worktreePruneExpire 설정으로 기간을 바꿀 수 있음). 하지만 석 달 동안 브랜치가 묶여 있는 건 불편하니, 손으로 지웠다면 바로 prune하는 습관을 들이세요. 더 좋은 건 처음부터 remove로 치우는 것입니다.
⚠️
실패한 add가 남기는 브랜치 — git worktree add ../cafe-menu.dessert -b feature/y처럼 이미 있는 폴더 경로에 새 브랜치로 만들려 하면 fatal: '../cafe-menu.dessert' already exists로 실패하는데, 재현해 보니 브랜치 feature/y는 이미 만들어진 채 남아 있었습니다. 같은 명령을 경로만 고쳐 다시 치면 이번에는 a branch named 'feature/y' already exists로 막힙니다. 이때는 -b 없이 git worktree add <새 경로> feature/y로 남은 브랜치를 꺼내면 됩니다.
8. 잠그기와 옮기기: lock·unlock·move·repair
8-1. lock: 가끔만 연결되는 곳에 둔 worktree
worktree를 외장 SSD나 네트워크 드라이브에 두는 경우가 있습니다. 디스크를 뽑아 둔 동안 누군가(또는 에이전트) git worktree prune을 치면, git은 "폴더가 없다"고 판단해 서랍을 치워 버립니다. 이것을 막는 것이 lock입니다.
bash
git worktree lock --reason "외장 SSD에서 작업 중" ../cafe-menu.dessert
git worktree list -v
text
/Users/minji/work/cafe-menu 2299675 [main]
/Users/minji/work/cafe-menu.dessert f8eea9e [feature/dessert]
locked: 외장 SSD에서 작업 중
/Users/minji/work/cafe-menu.search 3a0a784 [feature/search]
-v 없이 보면 줄 끝에 locked만 붙습니다. 잠긴 worktree는 폴더가 잠시 사라져도 prune이 건드리지 않습니다. 재현해 보니 폴더를 다른 이름으로 치워 둔 상태에서 git worktree prune -v를 쳐도 아무것도 지우지 않았습니다. 그리고 실수로 치우거나 옮기는 것도 막아 줍니다.
fatal: cannot remove a locked working tree, lock reason: 외장 SSD에서 작업 중
use 'remove -f -f' to override or unlock first
fatal: cannot move a locked working tree, lock reason: 외장 SSD에서 작업 중
use 'move -f -f' to override or unlock first
-f를 두 번 써야 뚫린다는 안내가 나옵니다. 그만큼 "정말로?"를 한 번 더 묻는 장치입니다. 잠금 정보는 개인 서랍 안의 locked 파일에 이유와 함께 적힙니다. 에이전트에게 오래 맡겨 둔 worktree를 실수로 치우지 않게 표시해 둘 때도 쓸 만합니다. git worktree add --lock으로 만들면서 바로 잠글 수도 있습니다.
8-2. move: 폴더를 옮길 때는 git에게 시킨다
디저트 폴더 이름을 cafe-menu.sweets로 바꾸고 싶습니다. 잠금을 풀고 move로 옮깁니다.
move는 폴더를 옮기면서 양쪽 안내문(.git 파일과 서랍의 gitdir)을 함께 고쳐 줍니다. 참고로 서랍 이름은 바뀌지 않습니다. ls .git/worktrees를 보면 여전히 cafe-menu.dessert이고, 옮긴 폴더의 .git 파일도 그 서랍을 가리킵니다. 서랍 이름은 내부 식별자일 뿐이라 신경 쓰지 않아도 됩니다. main worktree는 move로 옮길 수 없고, 서브모듈이 있는 worktree도 옮기지 못한다고 공식 문서에 적혀 있습니다.
8-3. 손으로 옮겼다면: repair (그리고 prune 주의)
Finder에서 폴더 이름을 바꿔 버렸다면 어떻게 될까요? cafe-menu.sweets를 손으로 다시 cafe-menu.dessert로 바꿔 봤습니다.
bash
mv ../cafe-menu.sweets ../cafe-menu.dessert
cd ../cafe-menu.dessert && git status
text
On branch feature/dessert
Your branch is up to date with 'origin/feature/dessert'.
nothing to commit, working tree clean
옮긴 폴더 안에서는 멀쩡해 보입니다. 폴더의 .git 파일이 가리키는 서랍은 제자리에 있으니까요. 그런데 main 폴더에서 목록을 보면 문제가 드러납니다.
서랍 쪽 안내문(gitdir)은 여전히 옛 이름 cafe-menu.sweets를 가리키니, git은 폴더가 사라졌다고 보고 prunable을 붙였습니다. 이 상태에서 prune을 치면, 멀쩡히 살아 있는 폴더의 서랍이 지워집니다. 그러면 옮긴 폴더는 서랍 없는 안내문만 남아 git 저장소가 아니게 됩니다(커밋해 둔 것은 브랜치에 남아 있으니 다시 worktree를 만들면 되지만, 커밋 안 한 스테이징 상태는 사라집니다). 그래서 목록에서 prunable을 보면 바로 prune하지 말고 "내가 옮긴 폴더는 아닌가?"를 먼저 떠올려야 합니다.
손으로 옮긴 경우의 처방은 repair입니다. 새 위치를 알려 주면 양쪽 안내문을 다시 이어 줍니다.
bash
git worktree repair ../cafe-menu.dessert
git worktree list
첫 줄은 "gitdir이 틀려 있어서 고쳤다"는 보고입니다. prunable이 사라졌습니다. 공식 문서에 따르면 main 폴더만 옮겼다면 옮긴 main 폴더에서 git worktree repair를, main 폴더와 linked 폴더를 함께 옮겼다면(예: ~/work 전체를 다른 곳으로 이동) main 폴더에서 git worktree repair <각 linked 폴더의 새 경로>를 쳐서 양방향 연결을 고칩니다. main worktree는 move로 옮길 수 없으니 이 경우는 repair가 유일한 길입니다. 애초에 폴더를 자주 옮길 계획이라면 git 2.48부터 생긴 --relative-paths 옵션(또는 worktree.useRelativePaths 설정)으로 안내문을 상대 경로로 적게 할 수 있습니다. 다만 공식 문서에 따르면 이 설정을 켜면 저장소에 extensions.relativeWorktrees가 켜져 옛 버전 git에서는 이 저장소를 열 수 없게 되니, 팀의 git 버전을 확인하고 쓰세요.
지금까지 본 명령을 아래 시뮬레이터에서 직접 눌러 보세요. 같은 브랜치를 두 번 꺼내기, 수정 중인 폴더 치우기, 손으로 지운 뒤 prune하기, 쓰는 중인 브랜치 지우기를 모두 해 볼 수 있습니다. 메시지는 모두 위에서 재현한 실제 문구입니다.
9. 어디에 두고 뭐라고 부를까: 이름과 위치 규칙
worktree 폴더는 어디든 만들 수 있지만, 규칙 없이 만들면 한 달 뒤 ~/work가 정체 모를 폴더로 가득해집니다. 널리 쓰이는 방식은 두 가지입니다.
폴더 목록을 이름순으로 정렬하면 같은 저장소의 worktree가 나란히 모이고, 폴더 이름만 봐도 무슨 작업인지 알 수 있습니다. 저장소 안이 아니라 밖에 있으니 .gitignore를 건드릴 필요도 없고, 편집기의 파일 검색이나 테스트 러너가 다른 worktree의 파일까지 훑는 일도 없습니다. 단점은 부모 폴더(~/work)가 어지러워진다는 것, 그리고 폴더가 저장소 밖에 흩어져 있어 "이 저장소의 worktree가 어디 있지?"를 git worktree list로 물어봐야 한다는 것입니다.
9-2. 저장소 안: .worktrees/<작업>
저장소 안의 숨김 폴더 하나에 모으는 방식도 있습니다. 3편에서 볼 Claude Code의 --worktree가 이와 같은 방식(.claude/worktrees/<이름>/)을 씁니다.
bash
git worktree add .worktrees/search-v2 -b feature/search-v2
git status --short
text
Preparing worktree (new branch 'feature/search-v2')
HEAD is now at 2299675 주문 웹앱 첫 버전
?? .worktrees/
main 폴더의 git status에 .worktrees/가 "추적하지 않는 파일"로 뜹니다. 누군가 git add .을 하면 사고가 날 수 있으니 반드시 무시 목록에 넣어야 합니다.
bash
echo'.worktrees/' >> .gitignore
git status --short
text
M .gitignore
이제 .worktrees/는 사라지고 .gitignore 수정만 남았습니다. 이 한 줄은 팀 모두에게 필요하니 커밋해 둡니다. 나 혼자만의 습관이라 팀 파일을 건드리기 싫다면 .git/info/exclude에 같은 줄을 적어도 됩니다(이 파일은 커밋되지 않는, 내 컴퓨터만의 무시 목록입니다).
비교
형제 폴더 ../cafe-menu.task
저장소 안 .worktrees/task
.gitignore 수정
필요 없음
필수 (안 하면 ?? .worktrees/)
한눈에 찾기
부모 폴더에 나란히
저장소 안 한 곳에 모임
편집기·검색 도구
서로 안 섞임
검색·감시 도구가 안쪽까지 훑을 수 있음
도구와의 궁합
Worktrunk 기본값에 가까움(3편)
Claude Code --worktree 방식(3편)
main 폴더를 지울 때
linked 폴더만 고아가 됨
linked 폴더까지 함께 사라짐
어느 쪽이든 괜찮습니다. 중요한 건 팀(과 에이전트)이 하나로 정하는 것입니다. 이 시리즈에서는 사람이 직접 만들 때는 형제 폴더, 에이전트 도구가 만들 때는 그 도구의 기본 위치를 따르기로 합니다.
브랜치 이름도 규칙이 있으면 좋습니다. 기존 시리즈 4편에서 정한 feature/…, fix/…에 더해, 에이전트가 맡은 브랜치는 agent/…로 시작하게 하면 git branch --list 'agent/*'로 에이전트 작업만 골라 볼 수 있습니다. 폴더 이름의 작업 부분과 브랜치 이름의 끝을 맞춰 두면(cafe-menu.search ↔ feature/search) 목록을 읽기가 훨씬 편합니다. 브랜치 이름 규칙은 4편에서 여러 Mac을 오갈 때 다시 다룹니다.
10. 실전 함정: node_modules, 포트, .env, 편집기
worktree 자체는 git이 알아서 해 주지만, 그 폴더에서 앱을 실제로 돌리려면 사람이 챙길 것이 있습니다. 5-7절에서 본 "따라오지 않는 것들"입니다.
10-1. worktree마다 npm install
새 worktree에는 node_modules/가 없습니다. 그래서 새 폴더에 들어가면 가장 먼저 의존성부터 설치해야 합니다.
bash
cd ~/work/cafe-menu.search
npm install
당연한 이야기 같지만 자주 잊습니다. 특히 에이전트에게 "테스트 돌려 봐"라고 했는데 vite: command not found 같은 에러가 나면 십중팔구 이것입니다. worktree가 다섯 개면 node_modules도 다섯 벌이라 디스크도 그만큼 씁니다. pnpm처럼 내용이 같은 파일을 한 곳에 모아 두고 링크로 쓰는 패키지 관리자를 쓰면 부담이 줄고, 3편의 Worktrunk는 Mac의 APFS 파일 시스템에서 node_modules 같은 무거운 폴더를 새 worktree와 나눠 쓰게 해 이 비용을 줄이는 방법을 제공합니다. 도구 버전(Node 몇 버전인지)까지 폴더마다 똑같이 맞추는 법은 6편의 mise.toml에서 다룹니다.
10-2. 개발 서버 포트가 겹친다
package.json의 개발 서버는 vite --port 5173으로 포트가 고정되어 있습니다. main 폴더에서 서버를 띄워 둔 채 search 폴더에서도 npm run dev를 치면 어떻게 될까요? 포트(port, 한 컴퓨터 안에서 프로그램이 네트워크 요청을 받는 번호)는 git이 아니라 컴퓨터 전체가 한 벌만 가진 자원이라 두 서버가 같은 번호를 동시에 쓸 수 없습니다.
어떤 컴퓨터에나 있는 파이썬 내장 웹 서버로 재현해 보겠습니다. 한 터미널에서 8765번 포트로 서버를 띄워 두고, 다른 터미널(다른 worktree)에서 같은 포트로 또 띄웁니다.
bash
# 터미널 1 (cafe-menu)
python3 -m http.server 8765 --bind 127.0.0.1
# 터미널 2 (cafe-menu.search)
python3 -m http.server 8765 --bind 127.0.0.1
text
Traceback (most recent call last):
...
OSError: [Errno 48] Address already in use
두 번째 서버는 "주소가 이미 쓰이는 중"이라며 죽습니다(가운데 긴 호출 기록은 생략했습니다). 포트 번호만 바꾸면 문제없이 뜹니다.
bash
python3 -m http.server 8766 --bind 127.0.0.1
text
Serving HTTP on 127.0.0.1 port 8766 (http://127.0.0.1:8766/) ...
vite는 설정에 따라 포트가 차 있으면 다음 번호로 자동으로 넘어가기도 하고(strictPort를 켜면 이 사례처럼 실패), 도구마다 동작이 다릅니다. 어느 쪽이든 문제는 "지금 브라우저에 보이는 게 어느 worktree의 서버인지" 헷갈린다는 것입니다. 에이전트가 "5173에서 확인했어요"라고 했는데 그게 다른 폴더의 서버였다면 확인은 무의미합니다. 해결책은 worktree마다 포트를 정해 두는 것입니다. 가장 단순하게는 실행할 때 포트를 넘기고(npm run dev -- --port 5174), 체계적으로는 폴더 이름에서 포트를 계산하는 방법이 있습니다. Worktrunk의 hash_port가 바로 이 일을 하는데, 3편에서 봅니다.
매번 이러기 귀찮고, 에이전트가 worktree를 만들 때마다 사람이 복사해 줄 수도 없습니다. 그래서 Claude Code는 .worktreeinclude라는 파일로 "새 worktree에 복사할 무시된 파일 목록"을 지정하게 해 줍니다. 3편의 첫 번째 주제입니다. 어떤 파일이 .env에 들어가야 하고 어떤 값은 아예 저장소 밖(비밀 관리 도구)에 두어야 하는지는 6편에서 정리합니다.
10-4. 편집기는 폴더마다 창 하나
VS Code, Cursor, JetBrains 같은 편집기는 폴더 단위로 프로젝트를 엽니다. worktree 하나를 열려면 그 폴더를 새 창으로 엽니다.
bash
code ~/work/cafe-menu.search
창이 여러 개가 되니, 지금 어느 창이 어느 worktree인지 헷갈리지 않게 창 제목(폴더 이름)을 확인하는 습관이 필요합니다. 형제 폴더 규칙(cafe-menu.search)이 여기서 빛을 발합니다. 창 제목만 봐도 작업이 보이니까요. 편집기의 git 패널도 그 창이 연 폴더의 브랜치와 스테이징을 보여 주므로, 5절에서 확인한 "파일·스테이징·HEAD는 폴더마다 따로"가 그대로 적용됩니다. 편집기마다 worktree를 만드는 전용 메뉴를 제공하기도 하지만, 여기서는 확인한 명령만 다룹니다.
🧳
새 작업대에 없는 것
node_modules, .env, 빌드 결과, 그리고 "내 포트". git은 커밋된 파일만 꺼내 줍니다.
🧰
만들자마자 하는 세 가지
① npm install ② 필요한 무시 파일 복사(.env) ③ 이 폴더의 포트 정하기. 매번 반복되니 스크립트나 도구로 자동화할 대상입니다.
🤖
에이전트에게 넘길 때
이 세 가지가 끝난 폴더를 넘기면 에이전트가 환경 문제로 헤매지 않습니다. 3편의 .worktreeinclude와 Worktrunk 훅이 이 자동화를 맡습니다.
11. switch·stash·clone과 비교하기: 언제 worktree인가
worktree가 좋다고 모든 일에 폴더를 늘릴 필요는 없습니다. 기존 방법과 나란히 놓고 보겠습니다.
한 폴더에서 git switch로 갈아타기는 가장 가볍습니다. 대신 고친 파일이 있으면 갈아탈 수 없거나(변경이 겹치는 파일이 있을 때), 고친 파일이 새 브랜치로 따라옵니다. 재현해 보면 이렇습니다.
bash
git switch fix/latte-price
text
error: Your local changes to the following files would be overwritten by checkout:
menu.md
Please commit your changes or stash them before you switch branches.
Aborting
git이 "커밋하거나 stash하라"고 권합니다. 그래서 등장하는 것이 git stash입니다. 고친 것을 잠깐 치워 두었다가 돌아와서 되살립니다. 몇 분짜리 급한 일에는 여전히 가장 빠른 방법이지만, stash가 쌓이면 무엇이었는지 잊기 쉽고, 5-5절에서 봤듯 모든 worktree가 한 목록을 공유합니다. 그리고 결정적으로, 갈아타는 동안 한 번에 한 가지 일만 할 수 있습니다.
clone을 하나 더 하면 완전히 독립된 저장소가 생깁니다. 설정·훅까지 따로라 실험에는 가장 안전하지만, 커밋을 주고받으려면 push·fetch를 거쳐야 하고 기록을 통째로 한 벌 더 받느라 느리고 무겁습니다. 그리고 두 clone이 같은 브랜치를 각자 꺼내 따로 고치는 걸 막아 주지 않아서, 나중에 push할 때 충돌로 알게 됩니다.
worktree는 그 사이의 균형점입니다. 기록은 공유해서 가볍고 즉시 보이며, 파일은 나눠서 동시에 여러 일을 할 수 있고, 같은 브랜치 이중 체크아웃은 구조적으로 막힙니다.
비교
git switch
git stash
git worktree
clone 하나 더
동시에 여러 작업
불가
불가 (번갈아)
가능
가능
디스크·시간
추가 없음
추가 없음
파일만 한 벌 더
기록까지 한 벌 더
다른 쪽 커밋 보기
—
—
즉시
push·fetch 필요
같은 브랜치 두 곳
—
—
git이 막음
막지 않음
설정·훅 분리
공유
공유
공유
완전 분리
node_modules·.env
그대로 씀
그대로 씀
폴더마다 챙김
폴더마다 챙김
잘 맞는 일
깨끗한 상태의 짧은 전환
몇 분짜리 급한 끼어들기
병렬 작업, 에이전트, 리뷰, 긴 테스트
다른 컴퓨터, 설정 실험
한 가지 덧붙이면, 다른 Mac은 worktree로 해결되지 않습니다. worktree는 한 컴퓨터의 한 저장소 안에서 작업대를 늘리는 기능이라, 맥북과 맥 스튜디오 사이는 각자 clone해 두고 push·fetch로 주고받아야 합니다. 이것이 1편의 두 번째 원칙이고, 4편의 주제입니다.
상황을 골라 어떤 방법이 맞는지 확인해 보세요.
에이전트와 함께 쓸 때 (3편 미리 보기)
이번 편은 순수한 git만 다뤘지만, 이 모든 것이 에이전트 병렬 작업의 바탕입니다. 민지가 Claude Code와 Codex에게 일을 나눠 맡긴다면, 사람이 직접 하는 버전은 이렇습니다.
그리고 각 폴더에서 npm install, .env 복사, 포트 정하기를 한 뒤, 한 터미널은 cafe-menu.search에서 Claude Code를, 다른 터미널은 cafe-menu.payment에서 Codex를 엽니다. 두 에이전트는 서로의 파일을 볼 일이 없고, 같은 브랜치를 건드릴 수도 없으며, 누가 커밋하든 main 폴더에서 git log --all로 바로 확인할 수 있습니다.
이번 편에서 본 규칙을 에이전트에게도 그대로 요구하면 됩니다. "네 폴더 밖의 파일은 건드리지 마", "worktree는 remove로만 치우고 --force는 쓰지 마", "prunable이 보여도 네가 prune하지 마". 3편에서는 이 과정을 Claude Code의 --worktree, 서브에이전트 격리, Worktrunk의 wt switch가 어떻게 자동으로 해 주는지 보고, 에이전트가 main 폴더를 건드리지 못하게 막는 법까지 다룹니다.
자주 하는 질문(FAQ)
Q1. worktree 폴더에서 git pull이나 git push를 해도 되나요?
됩니다. 원격 설정이 공유되므로 어느 worktree에서든 평소처럼 push·pull합니다. git fetch는 공용 창고의 origin/* 기록을 갱신하므로, 한 폴더에서 fetch하면 다른 모든 폴더에서도 새 원격 기록이 보입니다. 다만 pull은 "지금 폴더의 브랜치"에 합치는 것이니, 각 폴더에서 자기 브랜치를 pull하세요.
Q2. worktree를 몇 개까지 만들 수 있나요?
git 자체에 정해진 한계는 공식 문서에서 찾지 못했습니다. 현실적인 한계는 디스크(폴더마다 파일과 node_modules 한 벌)와 사람의 집중력입니다. 에이전트를 여러 개 돌리더라도 사람이 검토할 수 있는 만큼만 여는 것이 좋습니다. 끝난 작업은 7-3절 순서대로 바로 치우세요.
Q3. linked worktree에서 또 git worktree add를 해도 되나요?
됩니다. 어느 worktree에서 만들든 같은 공용 창고에 등록되고, 목록에도 똑같이 나타납니다. 다만 ../ 같은 상대 경로는 지금 있는 폴더 기준이니, 형제 폴더 규칙을 지키려면 main 폴더에서 만드는 편이 헷갈리지 않습니다.
Q4. worktree 폴더에서 git switch로 다른 브랜치로 갈아타도 되나요?
됩니다. 그 폴더의 HEAD만 바뀝니다. 단, 다른 worktree가 쓰고 있는 브랜치로는 갈아탈 수 없습니다(6절). 그런데 폴더 이름은 cafe-menu.search인데 안에서 다른 브랜치로 갈아타면 이름과 내용이 어긋나 헷갈립니다. "폴더 하나 = 브랜치 하나"로 두고, 다른 일이 생기면 새 worktree를 만드는 편이 이 시리즈의 원칙에 맞습니다.
Q5. 서브모듈이 있는 저장소에서도 쓸 수 있나요?
쓸 수는 있지만 제약이 있습니다. git 공식 문서의 BUGS 항목은 여러 worktree와 서브모듈을 함께 쓰는 지원이 아직 불완전하니 서브모듈 저장소에 여러 worktree를 만드는 것은 권하지 않는다고 적고 있고, 8-2절에서 봤듯 서브모듈이 든 worktree는 move로 옮길 수 없습니다. 서브모듈을 쓰는 저장소라면 먼저 작은 브랜치로 시험해 보세요.
Q6. 작업이 끝났는데 git worktree list에 prunable이 떠 있어요. 그냥 prune하면 되나요?
대부분은 그렇습니다. 하지만 먼저 git worktree prune --dry-run -v로 무엇이 지워질지 보고, 그 폴더를 손으로 옮긴 것은 아닌지 확인하세요. 옮긴 거라면 prune이 아니라 git worktree repair <새 위치>가 답입니다(8-3절). 외장 디스크에 있는 worktree라면 평소에 lock을 걸어 두면 이런 걱정이 없습니다.
이번 편 요약(치트시트)
하고 싶은 일
명령
기억할 점
새 브랜치로 작업대 만들기
git worktree add ../repo.task -b feature/task
기준점은 맨 뒤에(origin/main 등)
있는 브랜치 꺼내기
git worktree add ../repo.task 브랜치
원격에만 있으면 fetch 후 같은 이름으로
브랜치 없이 구경하기
git worktree add --detach ../repo.review 커밋
리뷰·테스트용, 다 보면 remove
목록 보기
git worktree list · -v · --porcelain
locked·prunable 꼬리표 확인
작업대 치우기
git worktree remove ../repo.task
수정 있으면 거절, --force는 확인 후에만
브랜치 지우기
git branch -d 브랜치
worktree를 먼저 치워야 지워짐
손으로 지운 폴더 정리
git worktree prune -n -v → prune -v
옮긴 폴더라면 prune 말고 repair
잠그기 / 풀기
git worktree lock --reason "…" 폴더 · unlock
잠기면 prune·remove·move 모두 막힘
옮기기
git worktree move 폴더 새폴더
손으로 옮겼다면 git worktree repair 새폴더
공유되는 것
커밋·브랜치·stash·설정·원격·훅
한 폴더의 커밋이 다른 폴더에 즉시 보임
폴더마다 따로
작업 파일·스테이징·HEAD
같은 브랜치는 한 폴더에서만
직접 챙길 것
npm install · .env · 포트
3편에서 자동화
다음 편 예고: 에이전트에게 worktree 맡기기
이번 편에서 민지는 worktree를 손으로 만들고, 들여다보고, 치우는 법을 익혔습니다. 그리고 새 작업대를 놓을 때마다 npm install, .env 복사, 포트 정하기를 반복해야 한다는 것도 알게 됐습니다.
3편 「에이전트에게 worktree 맡기기」에서는 이 반복을 도구에게 넘깁니다. Claude Code의 claude --worktree가 어디에 어떤 브랜치로 폴더를 만드는지, .worktreeinclude로 .env를 자동으로 따라오게 하는 법, 새 worktree의 기준점을 정하는 worktree.baseRef, 서브에이전트를 worktree로 격리하는 설정, 세션이 끝날 때의 정리 규칙을 공식 문서로 확인합니다. Codex가 worktree를 다루는 방식과, 이번 편의 명령을 한 줄로 묶어 주는 Worktrunk(wt)도 직접 실행해 봅니다. 이번 편에서 본 .git 파일, prunable, "already used by worktree"가 도구 뒤에서 그대로 등장하니, 기초를 알고 보면 훨씬 덜 신비롭습니다.
출처
Git 공식 문서, git-worktree: git-scm.com (로컬 git 2.55 git help worktree로 옵션·DETAILS·BUGS 확인)
Git 공식 문서, git-config의 gc.worktreePruneExpire, worktree.useRelativePaths, extensions.worktreeConfig: git-scm.com
Git 공식 문서, gitrepository-layout(worktrees/ 디렉터리): git-scm.com