에이전트 여럿, 저장소 하나 3편 — 에이전트에 worktree 맡기기: Claude Code·Codex·Worktrunk
2편에서 손으로 만들던 worktree를 이제 도구에게 맡깁니다. Claude Code의 --worktree·.worktreeinclude·worktree.baseRef·격리 강제·정리 규칙·서브에이전트 isolation, Codex 앱과 CLI가 worktree를 다루는 방식의 차이, 그리고 Worktrunk(wt)로 만들기부터 병합·정리까지 한 바퀴를 실제 출력으로 따라갑니다.
2편에서 민지는 git worktree add로 작업 폴더를 여러 개 만드는 법을 익혔습니다. 브랜치마다 폴더가 따로 있으니 Claude Code가 검색 기능을 만드는 동안 Codex가 영수증 버그를 고쳐도 서로의 파일을 건드리지 않습니다. 원칙 1, 작업 1개 = worktree 1개 = 브랜치 1개 = 에이전트 세션 1개가 손에 잡히기 시작한 것이죠.
그런데 며칠 해 보니 귀찮은 일이 매번 반복됩니다.
폴더 이름과 브랜치 이름을 매번 짓고 git worktree add ../cafe-menu-search -b feature/search를 칩니다.
새 폴더에는 .env가 없어서 앱이 뜨지 않습니다. 매번 복사합니다.
두 worktree에서 개발 서버를 동시에 켜면 둘 다 3000번 포트를 잡으려다 하나가 죽습니다.
일이 끝나면 병합하고, git worktree remove하고, 브랜치를 지웁니다. 가끔 잊어서 폴더가 쌓입니다.
도윤이 말했습니다. "그거 전부 도구가 해 줘요. Claude Code에도 있고, Codex에도 있고, 둘 다 쓰면 Worktrunk라는 것도 있어요." 이번 편은 그 세 가지를 차례로 봅니다. 무엇을 자동으로 해 주는지, 어디에 폴더를 만드는지, .env는 어떻게 옮기는지, 끝나면 무엇이 지워지고 무엇이 남는지를 공식 문서와 실제 실행 결과로 확인합니다.
이 글의 기준 (2026년 9월) — Claude Code는 공식 문서(code.claude.com/docs)와 로컬 2.1.283의 claude --help, Codex는 OpenAI 공식 문서(learn.chatgpt.com)와 Codex CLI 0.157.1의 codex --help·공개 소스, Worktrunk는 wt 0.79.0의 도움말과 공식 문서(worktrunk.dev)로 확인했습니다. 실제 에이전트 세션은 띄우지 않았습니다. Claude Code가 만드는 폴더 구조는 같은 모양을 git worktree add로 직접 만들어 git이 어떻게 보는지 확인했고, Worktrunk는 임시 폴더의 연습용 저장소에서 실제로 실행했습니다. 출력의 경로는 /Users/minji/code/...로, 가짜 원격은 https://github.com/minji/cafe-menu.git로 바꿔 적었습니다(해시와 구조는 그대로).
1. 도구가 대신 해 주는 세 가지 일: 만들기 · 준비하기 · 치우기
worktree를 호텔에 비유해 보겠습니다. 저장소의 .git 폴더는 호텔 프런트의 공용 장부입니다. 커밋과 브랜치 기록은 모두 여기에 한 권으로 적힙니다. worktree는 객실입니다. 손님(에이전트)마다 방을 하나씩 받고, 방 안의 가구(파일)는 마음대로 옮겨도 옆방에 영향이 없습니다. 하지만 장부는 하나라서, 어느 방에서 커밋하든 같은 장부에 기록됩니다(2편에서 본 "공유되는 것 vs 독립인 것").
사람이 git worktree add를 칠 때는 방 번호(폴더 경로)와 손님 이름표(브랜치 이름)를 직접 정합니다. 에이전트용 도구는 이 과정을 세 단계로 나눠 대신 처리합니다.
① 만들기 폴더 위치 · 브랜치 이름 · 어느 커밋에서 시작할지
→
② 준비하기 .env 같은 무시된 파일 복사 · 의존성 · 포트
→
③ 치우기 일이 없으면 지우고, 있으면 묻고, 병합 후 정리
도구마다 강한 단계가 다릅니다. 미리 요약하면 이렇습니다.
Claude Code는 ①과 ③이 촘촘합니다. --worktree 한 번으로 폴더와 브랜치를 만들고, 세션을 끝낼 때 지워도 되는지 스스로 검사합니다. 세션이 worktree 밖(메인 작업 폴더)을 건드리지 못하게 막기까지 합니다. ②는 .worktreeinclude로 무시된 파일을 복사하는 데까지입니다.
Codex는 데스크톱 앱이 worktree를 관리합니다. 앱에서는 ①②③이 모두 자동이고, CLI에는 비교적 단순한 --worktree 플래그가 있습니다. 둘의 동작이 꽤 다르니 따로 봐야 합니다.
Worktrunk(wt)는 특정 에이전트에 묶이지 않은 worktree 관리 CLI입니다. ②와 ③이 강합니다. 훅으로 .env 복사·포트 배정·의존성 설치를 자동화하고, wt merge 한 번으로 스쿼시·병합·정리까지 끝냅니다. 에이전트는 -x claude, -x codex처럼 "만든 뒤 실행할 프로그램"으로 붙입니다.
💡
용어 한 줄 풀이 — 메인 체크아웃(main checkout): 처음 clone한 원래 작업 폴더. worktree들의 "본관"입니다. 링크된 worktree: git worktree add로 만든 추가 작업 폴더. 폴더 안의 .git이 디렉터리가 아니라 본관 장부를 가리키는 한 줄짜리 파일입니다. 무시된 파일(ignored): .gitignore 규칙에 걸려 git이 추적하지 않는 파일. .env, node_modules/가 대표적입니다.
2. Claude Code --worktree: 한 줄로 격리된 세션 시작하기
Claude Code에서 가장 간단한 방법은 시작할 때 플래그 하나를 붙이는 것입니다. claude --help의 설명은 이렇습니다.
bash
claude --help | grep -A1 -- '-w, --worktree'
text
-w, --worktree [name] Create a new git worktree for this
session (optionally specify a name)
공식 문서(Run parallel sessions with worktrees)에 따르면 이름을 주면 저장소 루트 아래 .claude/worktrees/<이름>/에 worktree가 생기고, worktree-<이름>이라는 새 브랜치가 만들어집니다.
bash
cd ~/code/cafe-menu
claude --worktree search # 또는 claude -w search
다른 터미널에서 이름만 바꿔 한 번 더 실행하면 두 번째 격리 세션이 됩니다. 이름을 빼면 Claude Code가 bright-running-fox 같은 이름을 지어 줍니다. 몇 가지 알아 둘 점이 있습니다.
처음 여는 폴더면 먼저 claude를 한 번 실행해 작업 공간 신뢰(trust) 대화상자를 통과해야 합니다. 그렇지 않으면 --worktree가 오류로 끝납니다. 비대화형 실행(claude -p --worktree ...)은 신뢰 확인을 건너뜁니다.
--tmux를 함께 주면 그 worktree용 tmux 세션을 만들어 줍니다(--worktree가 있어야 동작, claude --help 기준).
데스크톱 앱에서는 세션을 시작할 때 브랜치 이름 옆의 worktree 옵션을 고르면 같은 일을 합니다. 앱은 설정에서 worktree 위치와 브랜치 접두사를 바꿀 수 있습니다.
git 쪽에서는 어떻게 보이나: 같은 모양을 직접 만들어 보기
실제 세션을 띄우지 않고, Claude Code가 만드는 것과 같은 위치·같은 브랜치 이름으로 worktree를 git 명령으로 만들어 봤습니다. git이 이 구조를 어떻게 보는지 확인하려는 것입니다(Claude Code 내부 구현을 재현한 것은 아닙니다).
Preparing worktree (new branch 'worktree-search')
HEAD is now at 45ee65f Initial order web app
?? .claude/
메인 작업 폴더의 git status에 ?? .claude/가 떴습니다. worktree 폴더가 저장소 안쪽에 있으니 git 눈에는 "추적 안 되는 새 폴더"로 보이는 겁니다. 그래서 공식 문서는 .claude/worktrees/를 .gitignore에 넣으라고 권합니다.
bash
echo'.claude/worktrees/' >> .gitignore
git status --short
git worktree list
cat .claude/worktrees/search/.git
text
M .gitignore
/Users/minji/code/cafe-menu 45ee65f [main]
/Users/minji/code/cafe-menu/.claude/worktrees/search 45ee65f [worktree-search]
gitdir: /Users/minji/code/cafe-menu/.git/worktrees/search
이제 .claude/는 사라지고 .gitignore 수정만 남았습니다(이 한 줄은 커밋해 두면 팀 전체에 적용됩니다). worktree 폴더의 .git은 본관 장부(.git/worktrees/search)를 가리키는 파일 한 줄입니다. 2편에서 본 링크된 worktree와 똑같습니다. Claude Code의 --worktree는 새로운 마법이 아니라 git worktree를 정해진 자리와 이름 규칙으로 만들어 주는 것이라는 점을 기억해 두면, 문제가 생겼을 때 git worktree list로 언제든 상태를 확인할 수 있습니다.
⚠️
새 worktree에는 추적되는 파일만 있습니다. 위 폴더에서 ls -A를 해 보면 .gitignore, index.html, menu.md, package.json, src뿐이고 .env도 node_modules도 없습니다. 공식 문서도 "worktree는 새 체크아웃이므로 개발 환경을 거기서 다시 초기화하라"고 말합니다. 의존성 설치는 Claude에게 부탁하거나 직접 하고, .env 같은 파일은 4장의 .worktreeinclude로 자동 복사합니다.
3. 어디서 출발할까: worktree.baseRef, PR에서 시작, 대화 중에 들어가기
worktree를 만들 때 가장 중요한 질문은 "어느 커밋에서 갈라져 나오나"입니다. Claude Code의 기본값은 의외일 수 있습니다. 지금 내가 있는 브랜치가 아니라 원격의 기본 브랜치(보통 origin/main)에서 출발합니다.
worktree.baseRef: "fresh"(기본) 또는 "head"
settings 레퍼런스에 따르면 worktree.baseRef는 두 값만 받습니다.
값
어디서 갈라지나
언제 쓰나
"fresh" (기본)
원격 기본 브랜치 origin/<기본 브랜치>. 원격과 똑같은 깨끗한 상태
새 기능·버그 수정처럼 main에서 새로 시작하는 일. 대부분의 경우
"head"
지금 로컬 HEAD. push 안 한 커밋과 기능 브랜치 상태까지 포함
진행 중인 기능 브랜치 위에서 서브에이전트에게 일부를 맡길 때
json
{"worktree":{"baseRef":"head"}}
세부 동작도 문서에 적혀 있습니다. "fresh"일 때 저장소를 최근 24시간 안에 fetch한 적이 없으면 기본 브랜치를 최대 5초 동안 fetch하고, 실패하면 로컬에 캐시된 ref를 씁니다. 원격이 아예 없거나 origin/HEAD를 알 수 없으면 로컬 HEAD로 대신합니다. worktree 안에서"head"는 메인 체크아웃이 아니라 그 worktree의 HEAD를 뜻합니다.
한 가지 제약이 있습니다. baseRef에 브랜치 이름은 넣을 수 없습니다. "도윤이 만든 feature/receipt 브랜치 위에서 시작하고 싶다"면 문서가 권하는 대로 git으로 직접 만드는 편이 낫습니다.
bash
git worktree add ../cafe-menu-receipt feature/receipt
cd ../cafe-menu-receipt
claude
이렇게 사람이 만든 worktree에서 그냥 claude를 실행해도 됩니다. 다만 6장에서 볼 자동 정리 대상에서는 빠집니다(내가 만든 것은 내가 치웁니다).
같은 settings 블록에는 baseRef 말고도 키가 세 개 더 있습니다. symlinkDirectories(예: ["node_modules"]를 적으면 메인 저장소의 그 폴더를 worktree마다 복사하지 않고 심볼릭 링크로 연결), sparsePaths(큰 모노레포에서 필요한 디렉터리만 체크아웃), bgIsolation(백그라운드 세션의 격리 방식)입니다. 이 글에서는 이름만 소개합니다.
PR 번호로 바로 시작: claude --worktree "#1234"
리뷰하거나 이어서 고칠 PR이 있으면 번호를 #과 함께 넘깁니다. Claude Code가 origin에서 그 PR의 head 커밋을 가져와 .claude/worktrees/pr-1234에 worktree를 만듭니다. #은 셸에서 주석 기호라서 반드시 따옴표로 감싸야 합니다.
bash
claude --worktree "#1234"
claude --worktree https://github.com/minji/cafe-menu/pull/1234
GitHub PR URL과 GitLab MR URL도 받지만, URL에서는 번호만 읽고 항상 origin에서 가져옵니다. github.com이면 pull/<번호>/head, gitlab.com이면 merge-requests/<번호>/head를 가져오고, 그 밖의 호스트(GitHub Enterprise, 자체 GitLab 등)는 두 경로를 차례로 시도합니다.
같은 이름을 다시 쓰면
이미 있는 이름으로 --worktree를 주면 새로 만들지 않고 그 worktree를 다시 엽니다. 기본값 "fresh"에서는 조건이 모두 맞을 때(변경 없음, Claude Code가 만든 브랜치 그대로, 자기 커밋이 없거나 PR이 병합되어 원격 브랜치가 지워짐) 기본 브랜치 최신으로 리셋해 줍니다. 그 외에는 예전 끝 커밋에서 그대로 엽니다. 병합이 끝난 작업 이름을 재활용해도 안전하게 설계되어 있다는 뜻입니다.
대화 도중에 "worktree에서 작업해"
이미 세션을 시작했더라도 Claude에게 "worktree에서 작업해(work in a worktree)"라고 말하면 EnterWorktree 도구로 worktree를 만들고 그리로 옮겨 갑니다. .claude/worktrees/ 아래의 다른 worktree로 바로 옮겨 갈 수도 있습니다. .claude/worktrees/밖의 경로로 들어가려 하면, 작업 폴더·쓰기 권한·CLAUDE.md 같은 프로젝트 설정이 통째로 그곳으로 넘어가기 때문에 매번 사람에게 승인을 받습니다. 권한 규칙이나 "다시 묻지 않기"로도 이 확인은 없어지지 않고, bypassPermissions 모드만 건너뜁니다.
🪝
훅 경로는 worktree를 따라가지 않습니다. worktree로 들어간 뒤에도 훅 설정 안의 ${CLAUDE_PROJECT_DIR}는 세션을 시작한 프로젝트 루트(메인 체크아웃)를 그대로 가리킵니다. 반면 훅이 입력으로 받는 JSON의 cwd 필드는 worktree 루트를 가리키고, Claude가 cd하면 따라 움직입니다. worktree 경로가 필요한 훅은 cwd를 읽어야 합니다.
4. .worktreeinclude: 무시된 파일 중 "고른 것만" 복사하기
새 worktree에 .env가 없어서 앱이 안 뜨는 문제를 풀 차례입니다. 방법은 저장소 루트에 .worktreeinclude라는 파일을 두는 것입니다. 공식 문서의 규칙은 두 문장입니다.
파일은 .gitignore와 같은 문법을 씁니다.
패턴과 일치하면서 동시에 git이 무시하는 파일만 복사합니다. 그래서 추적되는 파일은 절대 중복 복사되지 않습니다.
체에 비유하면, 두 개의 체를 겹쳐 놓고 둘 다 통과한 것만 새 방으로 옮기는 셈입니다. 첫 번째 체는 .gitignore("git이 모르는 파일"), 두 번째 체는 .worktreeinclude("가져가고 싶은 파일")입니다.
체 1 · .gitignore
작업 폴더에 있지만 git이 무시하는 파일만 남김 → .env, .env.local, node_modules/…
체 2 · .worktreeinclude
그중 패턴에 일치하는 것만 남김 → 추적 파일(menu.md)이나 무시되지 않은 새 파일(notes.txt)은 여기 적어도 탈락
이 규칙은 git 자체의 "무시 목록" 기능으로 흉내 낼 수 있습니다. 연습용 저장소에 .env, .env.local, node_modules/left-pad/index.js(모두 .gitignore로 무시됨)와, 무시되지 않은 새 파일 notes.txt를 두었습니다. 그리고 일부러 잘못된 줄까지 섞은 .worktreeinclude를 만들었습니다.
menu.md는 새 worktree에 이미 체크아웃되어 있으니 복사할 필요가 없고, notes.txt는 "git이 모르는 척하기로 한 파일"이 아니므로 가져가지 않습니다. 이 교집합 계산은 규칙을 이해하기 위한 재현이고, Claude Code가 내부적으로 정확히 이 명령을 쓴다는 뜻은 아닙니다. 다만 9장에서 쓸 Worktrunk의 wt step copy-ignored --require-include --dry-run에 같은 파일을 넣어 보니 똑같이 .env, .env.local 두 개만 복사하겠다고 답했습니다.
알아 둘 세부 규칙
적용 범위: Claude Code가 git으로 만드는 모든 worktree에 적용됩니다. --worktree, 서브에이전트 worktree, 데스크톱 앱의 병렬 세션 모두입니다.
WorktreeCreate 훅을 쓰면 처리되지 않습니다. 훅이 git 동작을 통째로 대신하기 때문에, .env 복사는 훅 스크립트 안에서 직접 해야 합니다(아래 참고).
**/로 시작하는 패턴과 통째로 무시된 폴더: 예를 들어 vendor/ 폴더 전체가 무시되어 있을 때 **/config.json만 적으면 그 안까지 닿지 않을 수 있습니다. 문서는 vendor/**/config.json처럼 폴더 이름을 패턴에 적으라고 권합니다.
node_modules/도 적을 수는 있지만 크기가 크면 매번 복사하는 비용이 듭니다. 이럴 때는 앞에서 소개한 worktree.symlinkDirectories로 링크하거나, 9장의 Worktrunk처럼 APFS의 copy-on-write 복사를 쓰는 방법이 있습니다.
text
# .worktreeinclude — 공식 문서 예시
.env
.env.local
config/secrets.json
🐘
Git LFS를 git lfs install --local로 설정했다면 Claude Code가 만든 worktree에는 실제 파일 대신 LFS 포인터 파일만 들어옵니다. 저장소 자체 설정(.git/config)에 들어 있는 필터 드라이버는 "누구든(Claude 포함) 저장소에 써 넣을 수 있는 셸 명령"이라서 worktree를 만들 때 실행하지 않기 때문입니다. 그 worktree 안에서 git lfs pull을 실행하면 됩니다. 전역 설정으로 한 평범한 git lfs install은 영향을 받지 않습니다. 대용량 파일을 어디에 둘지는 6편에서 다룹니다.
git이 아닌 방식이 필요할 때: WorktreeCreate 훅
SVN·Perforce·Mercurial을 쓰거나 worktree를 완전히 다른 곳에 두고 싶다면 WorktreeCreate 훅으로 생성 과정 전체를 바꿀 수 있습니다. 훅은 표준 입력으로 받은 JSON의 name을 읽고, 작업 폴더를 만든 뒤, 표준 출력의 마지막 줄에 그 절대 경로를 찍어야 합니다. 짝이 되는 WorktreeRemove 훅은 세션 종료나 서브에이전트 종료 때 worktree_path를 받아 정리합니다. git 저장소라면 대부분 기본 동작이면 충분하니, "이런 문이 있다" 정도로 알아 두면 됩니다. Worktrunk 도움말에도 wt switch --format json이 "Claude Code WorktreeCreate 훅 같은 도구 연동용"이라고 적혀 있지만, 이 조합은 직접 설정해 보지 않았습니다.
5. 격리 강제: worktree 안의 세션은 본관을 못 건드린다
폴더를 나눠 주는 것과, 에이전트가 정말 그 폴더 안에서만 일하게 하는 것은 다른 문제입니다. 에이전트가 cd ../..로 메인 체크아웃에 들어가 파일을 고치거나 git -C로 본관의 브랜치를 바꿔 버리면 격리가 무너집니다. Claude Code는 이것을 도구 호출 단계에서 막습니다. 공식 문서에 적힌 네 가지 검사는 다음과 같습니다.
검사
무엇을 막나
파일 편집
메인 체크아웃 안의 경로를 대상으로 하는 Edit·Write·NotebookEdit
명령 작업 폴더
작업 폴더가 메인 체크아웃으로 풀리거나, 밖에 머문다고 확인할 수 없는 Bash·PowerShell·Monitor 명령
git 리다이렉트
git -C, --git-dir, GIT_DIR·GIT_WORK_TREE 변수, 메인 체크아웃으로 cd한 뒤 git 실행 등으로 git을 본관에 돌리는 명령
명령 모양
명령 이름이 실행 중에 계산되는 등, 명령 글자만 보고 git이 worktree 안에 머무는지 확인할 수 없는 명령. 이 검사는 끌 수 없음
이 규칙은 --worktree로 시작했든, 대화 중 EnterWorktree로 들어갔든, worktree 세션을 재개했든 똑같이 적용됩니다. 격리된 세션이 띄우는 모든 서브에이전트에도 적용되고, 대화형·백그라운드를 가리지 않습니다. 거절되면 Claude는 어느 worktree 때문인지와 어떻게 고쳐 쓰면 되는지가 적힌 도구 오류를 받습니다. 예를 들어 여러 명령을 한 줄에 복잡하게 엮었다면 단순한 명령 여러 개로 나누라는 식입니다.
민지 입장에서 이 기능의 의미는 분명합니다. 메인 체크아웃에서 사람이 직접 npm run dev를 켜 놓고 확인하는 동안, 옆 worktree의 에이전트가 실수로라도 그 폴더의 파일을 바꾸거나 브랜치를 옮기지 못합니다. 2편에서 "같은 브랜치는 두 곳에 체크아웃할 수 없다"는 git의 규칙이 브랜치를 지켜 줬다면, 이 검사는 작업 폴더를 지켜 줍니다.
대신, 이것들은 본관과 공유된다
격리가 모든 것을 끊는 것은 아닙니다. 문서가 밝힌 공유 항목은 다음과 같습니다.
저장소의 .git 디렉터리: worktree에서 한 커밋은 본관 장부에 기록됩니다. 샌드박스를 켜도 이 쓰기는 허용되어 git commit이 동작합니다.
플러그인: 메인 체크아웃에서 프로젝트 범위로 설치한 플러그인은 같은 저장소의 worktree에서도 로드됩니다.
권한 승인: worktree 세션에서 Bash 명령에 "Yes, and don't ask again"을 고르면 그 규칙은 메인 체크아웃의 .claude/settings.local.json에 저장되어, 본관과 다른 모든 worktree에 적용되고 worktree를 지워도 남습니다(Windows 등 일부 경우 제외).
추적되지 않는 skills·agents·commands: .claude/skills를 gitignore해서 worktree 체크아웃에 그 폴더가 없으면, 메인 체크아웃의 프로젝트 skills를 읽어 옵니다. .claude/agents, .claude/commands도 마찬가지입니다.
이 공유 규칙은 --worktree로 만들었든, git worktree add로 직접 만들었든, 데스크톱 앱에서 만들었든 똑같이 적용됩니다.
6. 정리 규칙: 깨끗하면 지우고, 일이 남았으면 묻는다
worktree를 쓰다 보면 가장 흔한 사고는 두 가지입니다. 하나는 아직 필요한 작업을 지워 버리는 것, 다른 하나는 다 끝난 폴더가 수십 개 쌓이는 것입니다. Claude Code는 대화형 worktree 세션을 끝낼 때 지우면 사라질 것이 있는지 먼저 검사합니다. 변경되거나 추적 안 되는 파일, 체크아웃된 서브모듈 안의 미커밋 작업, 새 커밋이 검사 대상입니다.
종료 시 worktree 상태
Claude Code의 동작
깨끗함 (변경도 새 커밋도 없음)
이름 없는 세션이면 worktree와 브랜치를 자동 삭제. -n으로 이름 붙인 세션이면 나중에 쓰도록 남길지 먼저 물음
작업이 남아 있음
남길지 지울지 물음. 남기면 폴더와 브랜치 유지 + 종료 화면에 claude --worktree <이름> --resume 명령을 출력. 지우면 그 안의 작업도 함께 사라짐
상태를 확인할 수 없음
자동으로 지우지 않고, 무엇을 확인하지 못했는지 밝히며 물음
비대화형(-p) 실행
종료 질문이 없으므로 정리하지 않음. 만들 때 건 잠금(lock)도 남아 있다가 나중 세션의 정리가 풀어 줌. 직접 지우려면 git worktree remove, 잠겨 있으면 git worktree unlock 먼저
종료 화면에 나온 --resume 명령을 그대로 치면 같은 worktree로 돌아옵니다. worktree 안에서 끝난 세션을 재개하면 Claude Code가 그 폴더가 여전히 본관과 분리된 체크아웃인지 확인한 뒤 되돌려 보냅니다. 그 사이 폴더를 지웠다면 "worktree가 없다"고 알리고 실행한 폴더에서 이어 갑니다.
서브에이전트·백그라운드 worktree는 주기적 청소가 치운다
서브에이전트용 worktree는 변경 없이 끝나면 바로 지워집니다. 변경이 남은 것은 디스크에 남았다가 주기적 청소(sweep)가 처리합니다. 청소는 cleanupPeriodDays(기본 30일)보다 오래된 서브에이전트·백그라운드 세션 worktree를 지우지만, 다음 경우에는 건드리지 않습니다.
아직 작업이 남아 있음(변경·추적 안 되는 파일·push 안 한 커밋)
서브모듈에 변경이 있거나 검사할 수 없음
백그라운드로 보내지 않은 --worktree 세션의 worktree (나이와 무관)
사람이 git worktree add로 직접 만든 worktree — Claude Code는 자기가 만든 worktree의 git 메타데이터에 표식을 남기고, 표식이 없으면 지우지 않습니다
에이전트가 실행 중일 때는 그 worktree에 git worktree lock을 걸어 동시 정리로부터 보호하고, 끝나면 풉니다. 이 잠금은 2편에서 본 바로 그 lock입니다. 사람이 직접 건 잠금은 청소가 절대 풀지 않습니다.
7. 서브에이전트마다 worktree 하나: isolation: worktree
한 세션 안에서 Claude가 여러 서브에이전트(작업을 나눠 맡는 보조 에이전트)를 동시에 돌리면, 이들이 같은 폴더의 같은 파일을 동시에 고치다 충돌할 수 있습니다. 해결책은 서브에이전트에게도 각자 worktree를 주는 것입니다. 두 가지 방법이 있습니다.
대화 중에 "에이전트들에게 worktree를 쓰게 해(use worktrees for your agents)"라고 요청합니다.
항상 그렇게 동작하도록 커스텀 서브에이전트 파일(.claude/agents/*.md)의 frontmatter에 isolation: worktree를 적습니다.
markdown
---
name: refactorer
description: Applies mechanical refactors across many files
isolation: worktree
---
Apply the requested refactor across every affected file, then run the tests
and report the results.
이 서브에이전트는 임시 worktree에서 실행되고, 변경 없이 끝나면 그 worktree가 자동으로 지워집니다. 변경이 있으면 6장의 청소 규칙에 따라 안전해질 때까지 남습니다. 서브에이전트의 Bash·PowerShell 명령은 그 worktree 안에서 실행되고, 5장의 격리 검사가 똑같이 붙습니다.
여기서 흔히 놓치는 점이 있습니다. sub-agents 문서의 isolation 설명에 따르면 서브에이전트 worktree도 기본적으로 부모 세션의 HEAD가 아니라 기본 브랜치에서 갈라집니다(--worktree와 같은 worktree.baseRef 규칙). 민지가 feature/search 브랜치에서 작업하다가 "테스트 파일 정리는 서브에이전트에게 맡겨"라고 하면, 그 서브에이전트는 기본값에서는 민지의 진행 중인 커밋을 보지 못합니다. 진행 중인 작업 위에서 서브에이전트를 돌려야 한다면 worktree.baseRef를 "head"로 설정해야 합니다. 공식 문서도 "head"의 용도로 바로 이 경우를 듭니다.
✅
민지의 선택 — 새 기능은 claude -w 이름으로 main에서 깨끗하게 시작한다. 진행 중인 기능 위에서 서브에이전트에게 기계적인 리팩터링을 나눠 줄 때만 그 저장소의 .claude/settings.json에 "worktree": {"baseRef": "head"}를 넣는다. .claude/worktrees/는 .gitignore에, .env는 .worktreeinclude에 적고 둘 다 커밋한다.
8. Codex의 worktree: 앱과 CLI는 다르게 동작한다
이제 Codex입니다. 먼저 분명히 해 둘 것이 있습니다. OpenAI의 공식 문서 "Worktrees" 페이지는 ChatGPT 데스크톱 앱의 Codex를 기준으로 쓰여 있습니다. 터미널의 Codex CLI에도 --worktree 플래그가 있지만, 2026년 9월 기준으로 CLI 명령 레퍼런스 문서에서는 이 플래그를 찾지 못했습니다. 그래서 CLI 쪽은 codex --help와 공개 저장소(github.com/openai/codex)의 0.157.1 태그 소스로 확인한 내용이라고 밝혀 둡니다.
데스크톱 앱: 관리형 worktree
공식 문서의 내용을 정리하면 다음과 같습니다.
시작: 새 채팅 화면에서 입력창 아래의 Worktree를 고르고, 출발할 브랜치를 고른 뒤 프롬프트를 보냅니다. 출발점은 고른 브랜치의 HEAD 커밋이고, 커밋하지 않은 로컬 변경이 있는 브랜치를 고르면 그 변경도 worktree에 적용합니다.
위치: 기본은 $CODEX_HOME/worktrees 아래입니다. Settings > Worktrees의 Worktree root에서 바꿀 수 있습니다. Claude Code와 달리 저장소 밖에 두므로 .gitignore에 넣을 필요가 없습니다.
브랜치: 기본은 브랜치가 없는 detached HEAD 상태입니다. 브랜치를 어지럽히지 않고 worktree를 여러 개 만들기 위해서입니다. 이어서 커밋·push·PR까지 하고 싶으면 채팅 헤더의 Create branch here 버튼으로 브랜치를 만듭니다(화면 예시의 기본 접두사는 codex/).
Handoff: 채팅을 Local(원래 작업 폴더)과 Worktree 사이에서 옮깁니다. git은 한 브랜치를 한 곳에서만 체크아웃하게 하므로, 로컬에서 그 브랜치를 보고 싶으면 Handoff를 쓰라고 안내합니다. 같은 브랜치를 로컬에서 억지로 체크아웃하면 fatal: 'feature/a' is already used by worktree at '<WORKTREE_PATH>'가 난다는 예시도 문서에 있습니다(2편에서 본 규칙 그대로입니다).
.worktreeinclude: 저장소 루트에 두면 무시된 파일 중 일치하는 것만 복사합니다. Claude Code와 같은 규칙이고, 문서 예시도 .env, .env.local, config/secrets.json으로 같습니다. 원본이 심볼릭 링크면 건너뛰고, 새 체크아웃에 이미 있는 파일은 덮어쓰지 않습니다. 그리고 이 동작은 로컬 데스크톱 앱이 관리하는 worktree에만 적용되며, 원격 worktree나 사용자가 명령줄로 직접 만든 git worktree에는 적용되지 않는다고 명시합니다.
AGENTS.override.md: 무시된 AGENTS.override.md는 .worktreeinclude에 적지 않아도 관리형 worktree로 자동 복사됩니다. 개인용 에이전트 지침을 커밋하지 않고 쓰는 사람에게 편리합니다(AGENTS.md는 6편에서 다룹니다).
정리: 기본적으로 최근 15개의 관리형 worktree를 유지합니다. 채팅을 보관(archive)하거나 개수 한도를 넘으면 자동으로 지우되, 고정(pin)한 채팅·진행 중인 채팅·영구(permanent) worktree는 지우지 않습니다. 지우기 전에 스냅숏을 저장해 두었다가 채팅을 다시 열면 복원할 수 있게 해 줍니다. 오래 쓸 환경은 사이드바 프로젝트 메뉴에서 영구 worktree로 만들 수 있습니다.
준비 스크립트: "Local environments" 설정에서 worktree가 만들어질 때 자동 실행할 setup 스크립트(예: npm install, npm run build)를 정할 수 있습니다. 설정은 프로젝트 루트의 .codex 폴더에 저장되어 저장소에 커밋해 공유할 수 있고, 이 기능은 데스크톱 앱 전용입니다.
CLI 0.157.1: --worktree 플래그와 /worktree
로컬에 설치된 Codex CLI의 도움말에서 worktree와 관련 있는 줄만 뽑았습니다.
-s, --sandbox <SANDBOX_MODE>
Select the sandbox policy to use when executing model-generated shell commands
--
-C, --cd <DIR>
Tell the agent to use the specified directory as its working root
--
--worktree
Run the session in a new managed Git worktree
--
--add-dir <DIR>
Additional directories that should be writable alongside the primary workspace
--sandbox가 받는 값은 read-only, workspace-write, danger-full-access 세 가지입니다(같은 도움말).
codex exec(비대화형)의 도움말에도 같은 --worktree가 있고, codex features list에서 worktrees 기능은 stable·true로 켜져 있었습니다. CLI 화면 안에서는 /worktree 슬래시 명령("start or continue a conversation in a new worktree")도 소스에 있습니다. Claude Code의 --worktree와 달리 이름을 받지 않는 켜기/끄기 플래그입니다.
0.157.1 태그의 소스(codex-rs/worktree)를 읽어 확인한 CLI 동작은 이렇습니다. 실제 세션을 띄워 확인한 것은 아니므로 버전이 바뀌면 달라질 수 있습니다.
앱과 같은 루트($CODEX_HOME/worktrees, 앱 설정의 Worktree root가 있으면 그것)를 씁니다. 그 아래에 무작위 4글자 폴더를 만들고 그 안에 저장소 이름으로 체크아웃합니다.
git worktree add --detach로 현재 HEAD 커밋에서 detached 상태로 만듭니다. 커밋된 상태만 들어가므로, 앱과 달리 미커밋 변경은 따라오지 않는다고 보는 것이 안전합니다.
설정 코드에 "CLI allocation uses the same root without enabling automatic cleanup"이라는 주석이 있습니다. 즉 CLI가 만든 worktree는 자동으로 정리되지 않습니다.
worktree 생성 코드에서 .worktreeinclude를 처리하는 부분은 찾지 못했습니다. 공식 문서도 앱 관리형 worktree에만 적용된다고 하니, CLI에서는 .env를 직접 복사해야 한다고 가정하세요.
그래서 Codex CLI로 여러 작업을 병렬로 돌릴 때는, 폴더 이름과 브랜치 이름을 내가 정하는 git worktree + -C 조합이 오히려 다루기 쉽습니다. 이것은 공식 문서에 정리된 CLI 플래그만으로 되는 방식입니다.
--sandbox workspace-write는 Codex 샌드박스 문서의 표현으로 "파일을 읽고, 작업 공간 안에서 편집하고, 그 경계 안에서 일상적인 로컬 명령을 실행"하는 모드입니다. 여기서 작업 공간은 -C로 준 worktree입니다. 9장의 Worktrunk를 쓰면 이 세 줄이 한 줄(wt switch -c -x codex agent/receipt-codex)로 줄어듭니다.
항목
Codex 데스크톱 앱
Codex CLI 0.157.1
시작 방법
입력창 아래 Worktree 선택 + 출발 브랜치 선택
codex --worktree, 화면 안 /worktree
위치
~/.codex/worktrees 기본(CODEX_HOME 기준), 설정에서 변경
앱과 같은 루트 (소스 확인)
브랜치
detached HEAD, 필요하면 Create branch here
detached HEAD (소스 확인)
미커밋 변경
고른 브랜치의 미커밋 변경도 적용
커밋 상태만 (소스로 추정)
.worktreeinclude
적용 (공식 문서)
문서상 적용 대상 아님, 소스에서도 처리 코드 못 찾음
AGENTS.override.md
무시된 파일이면 자동 복사
직접 확인 못 함
정리
최근 15개 유지, 보관 시 삭제, 삭제 전 스냅숏
자동 정리 꺼짐 (소스 주석)
준비 스크립트
Local environments의 setup 스크립트
없음 → 직접 또는 Worktrunk 훅
9. Worktrunk 실습: 만들기부터 병합·정리까지 한 바퀴
Claude Code와 Codex를 둘 다 쓰는 민지에게 필요한 것은 "어느 에이전트든 같은 방식으로 방을 만들고 치우는" 도구입니다. Worktrunk(wt)가 그 자리에 있습니다. 공식 설명은 "병렬 AI 에이전트 작업 흐름을 위해 설계된 git worktree 관리 CLI"이고, 라이선스는 MIT 또는 Apache-2.0입니다. 명령은 네 개가 중심입니다.
bash
wt --help | sed -n '1,12p'
text
wt - Git worktree management for parallel AI agent workflows
Usage: wt [OPTIONS] [COMMAND]
Commands:
switch Switch to a worktree; create if needed
list List worktrees and their status
remove Remove worktree; delete branch if merged
merge Merge current branch into the target branch
step Run individual operations
hook Run configured hooks
config Manage user & project configs
아래는 모두 연습용 저장소에서 실제로 실행한 결과입니다. 사용자 설정을 건드리지 않으려고 HOME을 임시 폴더로 바꿔서 진행했습니다.
9-1. 셸 연동: 왜 필요한가
wt switch는 worktree를 만든 뒤 내 터미널을 그 폴더로 옮겨 줍니다. 그런데 여기에는 운영체제의 기본 규칙이 걸립니다. 프로그램(자식 프로세스)은 자기를 실행한 셸(부모)의 현재 폴더를 바꿀 수 없습니다. 그래서 Worktrunk는 셸에 wt라는 함수를 심고, 실제 프로그램이 "이 폴더로 가라"고 파일에 적어 두면 함수가 그 경로로 cd하는 방식을 씁니다.
bash
wt config shell install
cat ~/.zshrc
text
✓ Added shell extension & completions for zsh @ ~/.zshrc
↳ Skipped bash; ~/.bashrc not found
✓ Configured 1 shell
if command -v wt >/dev/null 2>&1; then eval "$(command wt config shell init zsh)"; fi
wt config shell init zsh가 내놓는 코드에는 "Override wt command so it can change the parent shell's directory"라는 주석과 함께 임시 파일에 적힌 경로로 builtin cd하는 함수가 들어 있습니다. 연동 없이 실행하면 worktree는 만들어지지만 이런 경고가 뜨고 터미널은 제자리에 있습니다.
text
▲ Cannot change directory — shell integration not installed
↳ To enable automatic cd, run wt config shell install
9-2. 프로젝트 설정: .worktreeinclude와 .config/wt.toml
민지는 저장소에 두 파일을 추가해 커밋했습니다. .worktreeinclude에는 복사할 무시된 파일을, .config/wt.toml(Worktrunk의 프로젝트 설정 파일)에는 훅을 적습니다.
bash
cat .worktreeinclude
cat .config/wt.toml
text
.env
node_modules/
# cafe-menu/.config/wt.toml — Worktrunk 프로젝트 설정
[pre-start]
copy = "wt step copy-ignored --require-include"
[post-start]
port = "echo PORT={{ branch | hash_port }} > .env.local"
각 줄의 뜻은 이렇습니다.
pre-start: 새 worktree가 만들어질 때 한 번, 끝날 때까지 기다리며 실행합니다. 여기서 wt step copy-ignored가 메인 worktree의 무시된 파일을 새 worktree로 복사합니다. --require-include를 붙이면 .worktreeinclude에 적힌 것만 복사합니다. 붙이지 않으면 Worktrunk는 무시된 파일 전부를 복사하는데(도움말에 "worktrunk copies all gitignored files by default; Claude Code requires .worktreeinclude"라고 차이를 밝혀 둠), 이 플래그로 Claude Code·Codex 앱과 같은 규칙에 맞춥니다.
post-start: 새 worktree가 만들어진 뒤 백그라운드에서 실행합니다. 여기서는 브랜치 이름으로 포트 번호를 만들어 .env.local에 적습니다. hash_port는 문자열을 10000~19999 사이 포트로 바꾸는 템플릿 필터라서, 같은 브랜치는 언제나 같은 포트를 받습니다.
Worktrunk 0.79의 훅 이름은 pre-switch/post-switch, pre-start/post-start(만들 때), pre-commit/post-commit, pre-merge/post-merge, pre-remove/post-remove입니다. pre-*는 끝날 때까지 기다리고 실패하면 작업을 멈추며, post-*는 백그라운드에서 돕니다. 훅을 확인하는 명령도 있습니다.
"requires approval"에 주목하세요. 저장소에 들어 있는 프로젝트 훅은 처음 실행할 때 사람의 승인을 받습니다. 누군가 저장소에 악성 명령을 넣어 두는 경우를 막기 위해서입니다. 승인은 ~/.config/worktrunk/approvals.toml에 저장되고, 명령이 바뀌면 다시 묻습니다. 아래 실습에서는 프롬프트를 건너뛰는 --yes를 붙였습니다. 평소에는 붙이지 말고 명령을 읽은 뒤 승인하세요.
📦
의존성 설치도 훅으로. Worktrunk 도움말은 [pre-start]에 install = "npm ci"처럼 설치 명령을 두는 예와, [[post-start]] 블록을 여러 개 이어 "설치가 끝난 뒤 빌드와 개발 서버를 동시에" 돌리는 파이프라인 예를 보여 줍니다. 이 실습 저장소에는 설치할 패키지가 없어서 실행하지는 않았습니다. 설치 대신 node_modules/를 .worktreeinclude에 적어 복사하는 방법은 바로 아래에서 봅니다.
9-3. wt switch -c: 만들고, 복사하고, 이동하기
bash
wt switch --yes -c feature/search
pwdls -A
text
◎ Running pre-start project:copy
wt step copy-ignored --require-include
✓ Copied 2 files · 40 B (reflinked, no extra disk)
✓ Created branch feature/search from main and worktree @ /Users/minji/code/cafe-menu.feature-search
↳ To customize worktree locations, run wt config create
◎ Running post-start: port (project)
/Users/minji/code/cafe-menu.feature-search
.config
.env
.git
.gitignore
.worktreeinclude
index.html
menu.md
node_modules
package.json
src
한 줄로 네 가지가 끝났습니다. ① main에서 feature/search 브랜치를 만들고, ② 저장소 옆 폴더 cafe-menu.feature-search에 worktree를 만들고(Worktrunk의 기본 위치 규칙은 <저장소>.<브랜치>이고 /는 -로 바뀜), ③ .env와 node_modules를 복사하고, ④ 터미널을 그 폴더로 옮겼습니다.
복사 줄의 "reflinked, no extra disk"가 중요합니다. macOS의 APFS 같은 파일 시스템에서 Worktrunk는 파일을 reflink(copy-on-write 복사)로 만듭니다. 복사본이 원본과 같은 디스크 블록을 공유하다가, 어느 한쪽이 수정할 때만 실제로 갈라집니다. 도움말에는 14GB짜리 Rust target/ 폴더를 cp -R로 복사하면 2분·14GB가 들지만 reflink로는 20초·추가 공간 거의 0이라는 표가 있습니다(Worktrunk 측 수치, 직접 재지는 않음). 거대한 node_modules나 빌드 캐시를 worktree마다 "공짜에 가깝게" 들고 갈 수 있는 이유입니다. ext4·NTFS처럼 reflink가 없는 파일 시스템에서는 전체 복사가 일어나고 요약에 "(full copy)"라고 나옵니다.
9-4. -x: 만든 뒤 에이전트 실행하기
-x(--execute)는 "worktree로 옮긴 뒤 실행할 프로그램"입니다. 실습에서는 에이전트를 띄우는 대신 무해한 pwd로 동작만 확인했습니다.
bash
wt switch --yes -c -x pwd fix/receipt
text
◎ Running pre-start project:copy
wt step copy-ignored --require-include
✓ Copied 2 files · 40 B (reflinked, no extra disk)
✓ Created branch fix/receipt from main and worktree @ /Users/minji/code/cafe-menu.fix-receipt
◎ Running post-start: port (project)
◎ Executing (--execute):
pwd
/Users/minji/code/cafe-menu.fix-receipt
pwd가 새 worktree 경로를 찍었습니다. 즉 -x로 준 프로그램은 새 worktree 안에서 실행됩니다. 실제로는 pwd 자리에 에이전트를 넣습니다.
bash
wt switch -c -x claude feature/search # worktree를 만들고 그 안에서 Claude Code 시작
wt switch -c -x codex fix/receipt # 같은 방식으로 Codex CLI 시작
wt switch -c -x claude feature/search -- '검색창을 추가해 줘'# -- 뒤는 claude에 그대로 전달
-- 뒤의 인자는 그 프로그램에 그대로 넘어가므로, 세 번째 줄은 claude '검색창을 추가해 줘'를 실행하는 것과 같습니다. 도움말은 alias wsc='wt switch --create -x claude'처럼 별칭을 만들어 두는 방법도 소개합니다. 브랜치 인자 없이 wt switch -x claude만 치면 대화형 선택 화면에서 worktree를 고른 뒤 거기서 Claude Code를 띄웁니다.
🧩
-x claude와 claude --worktree는 겹치지 않게. Worktrunk가 이미 worktree를 만들고 그 안에서 claude를 실행하므로, 여기에 --worktree를 또 붙이면 worktree 안에 또 worktree를 만드는 셈이 됩니다. 한 작업에는 한 도구만 worktree를 만들게 하세요. Worktrunk로 만든 worktree는 Claude Code 입장에서 "사람이 git worktree add로 만든 것"이라, 6장의 자동 청소 대상이 아니고 정리는 wt remove/wt merge가 맡습니다.
9-5. hash_port: worktree마다 다른 개발 서버 포트
백그라운드 post-start 훅이 끝난 뒤 세 폴더의 .env.local을 봤습니다.
bash
cat .env.local # feature/searchcat ../cafe-menu.fix-receipt/.env.local
cat ../cafe-menu/.env.local # 메인 worktree (원래 있던 파일)
text
PORT=14568
PORT=18417
PORT=3000
브랜치마다 다른 포트가 적혔습니다. 앱이 .env.local의 PORT를 읽는다면, 메인 폴더(3000)와 두 에이전트의 폴더(14568, 18417)에서 동시에 개발 서버를 켜도 부딪히지 않습니다. 도움말은 훅에서 바로 서버를 띄우는 예(dev = "npm run dev -- --port {{ branch | hash_port }}")도 보여 줍니다. 포트는 해시로 정하므로 드물게 두 브랜치가 같은 포트를 받을 수는 있습니다. 이 방식은 2편에서 손으로 하던 "worktree별 포트 나누기"를 자동으로 해 주는 것입니다.
9-6. wt list: 방마다 상태 한눈에 보기
feature/search에서 커밋을 두 개 하고, fix/receipt에서는 파일 하나를 고친 채 커밋하지 않았습니다. 메인 worktree에서 목록을 봤습니다.
bash
wt list
text
Branch Status HEAD± main↕ main…± Remote⇅ Path Commit Age Message
@ main ^| | . 688a312 now Add worktree setup for agents
+ feature/search ↑ ↑2 +2 ../cafe-menu.feature-search 0939fae now Allow one-letter search
+ fix/receipt ! – +1 ../cafe-menu.fix-receipt 688a312 now Add worktree setup for agents
○ Showing 3 worktrees, 1 with changes, 1 ahead
wt list --help의 기호 설명으로 읽으면 이렇습니다.
줄
읽는 법
@ main ^|
@ 지금 있는 worktree, ^ 저장소의 본관 worktree, | 원격과 같음
+ feature/search ↑ ↑2 +2
+ 다른 worktree, ↑ main에 없는 커밋이 있음, main↕ 열의 ↑2 두 커밋 앞섬, main…± 열의 +2 main과 갈라진 뒤 두 줄 추가
+ fix/receipt ! – +1
! 커밋 안 한 수정 있음, – main과 같은 커밋인데 미커밋 변경이 있음, HEAD± 열의 +1 미커밋 한 줄 추가
에이전트 여러 개를 돌릴 때 이 표 하나로 "누가 일을 끝냈고(↑, 커밋 있음), 누가 아직 작업 중이고(!), 누가 아무것도 안 했는지(_ 표시, 지워도 안전)"를 볼 수 있습니다. 도움말에는 _("같은 커밋, 깨끗함 — 지워도 안전")과 ⊂("다른 이력으로 이미 통합됨") 줄은 흐리게 표시된다고 나옵니다.
9-7. wt merge main: 스쿼시·병합·정리를 한 번에
feature/search가 끝났습니다. 그 worktree 안에서 병합합니다. git merge와 방향이 반대라는 점에 주의하세요. wt merge main은 "main을 가져와라"가 아니라 "지금 브랜치를 main에 넣어라"입니다. GitHub의 "Merge pull request" 버튼을 로컬에서 누르는 것과 비슷합니다.
bash
cd ../cafe-menu.feature-search
wt merge main
text
◎ Squashing 2 commits into a single commit (1 file, +2)...
◎ Generating squash commit message...
↳ Using fallback commit message. For LLM setup guide, run wt config --help
Squash commits from feature/search
Combined commits:
- Add menu search
- Allow one-letter search
✓ Squashed @ ce01201
◎ Merging 1 commit to main @ ce01201 (no rebase needed)
* ce01201 Squash commits from feature/search
src/search.js | 2 ++
1 file changed, 2 insertions(+)
✓ Merged to main (1 commit, 1 file, +2)
◎ Removing feature/search worktree & branch in background (same commit as main, _)
○ Switched to worktree for main @ /Users/minji/code/cafe-menu
도움말의 파이프라인대로 진행되었습니다. ① 커밋 두 개를 하나로 스쿼시(커밋 메시지는 LLM 연동을 설정하지 않아 기본 문구가 쓰임), ② main 위로 rebase(이번엔 필요 없음), ③ pre-merge 훅(이번엔 없음), ④ main을 빨리 감기(fast-forward) 병합, ⑤ worktree와 브랜치를 백그라운드에서 삭제, ⑥ 터미널을 main worktree로 옮김. 커밋을 살리고 싶으면 --no-squash, worktree를 남기려면 --no-remove, 병합 커밋을 만들려면 --no-ff를 씁니다. pre-merge 훅에 테스트를 걸어 두면 테스트가 실패할 때 병합 자체가 멈춥니다.
bash
git log --oneline --graph -3
git push
text
* ce01201 Squash commits from feature/search
* 688a312 Add worktree setup for agents
* 45ee65f Initial order web app
To https://github.com/minji/cafe-menu.git
688a312..ce01201 main -> main
wt merge는 로컬 병합입니다. 원격에 올리는 것은 여전히 사람이 git push로 합니다. 팀에서 main 보호 규칙과 PR 리뷰를 쓴다면 wt merge 대신 브랜치를 push해서 PR을 여는 흐름이 맞습니다(PR과 보호 규칙은 6편에서 다룹니다). 혼자 쓰는 저장소나 에이전트 결과를 로컬에서 빠르게 합칠 때 특히 편합니다.
9-8. wt remove: 일이 남은 방은 지우지 않는다
fix/receipt는 결국 필요 없어졌습니다. 지워 봅니다.
bash
wt remove fix/receipt
text
✗ Cannot remove worktree: fix/receipt has uncommitted changes
M src/order.js
↳ Commit or stash changes first, or to lose uncommitted changes, run wt remove --force fix/receipt
커밋 안 한 수정이 있어서 거절했습니다. Claude Code의 종료 검사처럼 "지우면 사라질 것이 있으면 멈춘다"는 원칙입니다. 수정을 버리고 다시 지웁니다.
◎ Removing fix/receipt worktree & branch in background (ancestor of main, ⊂)
/Users/minji/code/cafe-menu ce01201 [main]
* main
이번에는 worktree와 브랜치가 함께 지워졌습니다. fix/receipt에는 main에 없는 커밋이 없었기 때문입니다(ancestor of main). wt remove는 병합된 브랜치만 함께 지우고, 병합 안 된 커밋이 있는 브랜치는 남깁니다. 그것까지 지우려면 -D(--force-delete)를 명시해야 합니다. 개발 서버처럼 그 폴더에서 돌던 프로세스까지 끄는 --reap(실험 기능)도 있습니다.
10. 한눈에 비교: git · Claude Code · Codex · Worktrunk
지금까지 본 네 가지 방법을 같은 질문으로 정리했습니다.
질문
plain git
Claude Code --worktree
Codex (앱 / CLI)
Worktrunk wt
worktree 위치
내가 정함 (보통 저장소 옆)
저장소 안 .claude/worktrees/<이름>/ (앱은 설정 가능)
~/.codex/worktrees/… (저장소 밖, 앱 설정 가능 / CLI도 같은 루트)
기본 <저장소>.<브랜치> (저장소 옆), worktree-path 템플릿으로 변경
브랜치 이름
내가 정함
worktree-<이름>, PR은 pr-<번호> 폴더
detached HEAD, 필요하면 Create branch here(앱)
내가 준 이름 그대로 (feature/search)
출발점
지정한 커밋·브랜치
기본 origin 기본 브랜치(fresh), head로 변경
앱: 고른 브랜치(+미커밋 변경) / CLI: 현재 HEAD
기본 브랜치, --base로 변경(@ 현재, pr:N)
.env 복사
직접 cp
.worktreeinclude (무시됨 ∩ 일치)
앱: .worktreeinclude + AGENTS.override.md / CLI: 직접
wt step copy-ignored 훅, reflink 복사
의존성·포트
직접
Claude에게 부탁, symlinkDirectories
앱: setup 스크립트 / CLI: 직접
훅 + hash_port
격리 강제
없음
본관 편집·명령·git 리다이렉트 차단
샌드박스(workspace-write) 범위
없음 (에이전트 쪽 설정에 맡김)
정리
worktree remove + branch -d
종료 시: 깨끗하면 자동 삭제, 작업 있으면 질문 / 서브에이전트는 주기적 청소
앱: 최근 15개 유지·스냅숏 / CLI: 자동 정리 없음
wt merge(스쿼시·병합·삭제), wt remove(미커밋이면 거절)
에이전트 실행
폴더로 가서 직접
플래그 자체가 세션 시작
앱 화면 / codex --worktree, -C
-x claude, -x codex
여러 에이전트
가능하지만 손이 많이 감
Claude 세션·서브에이전트끼리 최적
앱 안의 Codex 채팅끼리 최적
Claude·Codex 섞어 쓸 때 공통 도구
민지는 이렇게 정했습니다. Claude Code만 쓰는 작업은 claude -w로 시작해 격리 검사와 자동 정리의 이점을 누리고, Claude와 Codex를 함께 돌리거나 같은 일을 둘에게 시키는 날은 Worktrunk로 방을 만들고 치웁니다. 어느 쪽이든 .worktreeinclude는 같은 파일 하나로 공유됩니다. 두 에이전트에게 같은 일을 시키고 결과를 비교하는 방법은 5편에서 이어집니다.
치트시트
하고 싶은 일
명령·설정
Claude Code를 새 worktree에서 시작
claude -w search → .claude/worktrees/search, 브랜치 worktree-search
Q. .worktreeinclude를 커밋해야 하나요?
네, 커밋하는 편이 좋습니다. 파일 안에는 비밀 값이 아니라 "어떤 파일을 복사할지"라는 패턴만 들어 있습니다. 커밋해 두면 도윤의 Mac에서도, Codex 앱에서도, Worktrunk에서도 같은 규칙이 적용됩니다. 실제 .env는 여전히 .gitignore에 있어야 합니다. 거꾸로 .env를 추적하기 시작하면 .worktreeinclude에 적어도 "추적되는 파일"이라서 복사 대상에서 빠집니다(이미 체크아웃되니까요). 대신 비밀이 원격에 올라가는 더 큰 문제가 생깁니다.
Q. claude --worktree로 만든 worktree에 .env가 여전히 없어요.
세 가지를 확인하세요. ① .worktreeinclude가 저장소 루트에 있는지, ② 복사하려는 파일이 정말 git이 무시하는 파일인지(git check-ignore -v .env로 어느 규칙에 걸리는지 확인), ③ WorktreeCreate 훅을 쓰고 있지 않은지(훅을 쓰면 .worktreeinclude는 처리되지 않습니다). 복사는 worktree가 만들어질 때 일어나므로, 이미 있는 worktree에는 직접 복사해야 합니다.
Q. 서브에이전트가 제가 방금 한 커밋을 못 봐요.
서브에이전트 worktree도 기본적으로 원격 기본 브랜치에서 갈라지기 때문입니다(worktree.baseRef가 "fresh"). 진행 중인 작업 위에서 서브에이전트를 돌리려면 "head"로 바꾸세요. "fresh"는 원격 기준이므로, 이미 push한 커밋이라도 main에 병합되지 않았다면 보이지 않습니다.
Q. Codex CLI에서 --worktree를 썼더니 폴더가 계속 쌓입니다.
0.157.1 소스 기준으로 CLI가 만든 관리형 worktree는 자동 정리가 꺼져 있습니다. git worktree list로 ~/.codex/worktrees/… 아래의 항목을 확인하고 git worktree remove로 지우세요. 앱을 함께 쓴다면 앱의 정리 설정이 CLI worktree에도 적용되는지는 직접 확인하지 못했습니다. 이름과 위치를 내가 관리하고 싶다면 git worktree add + codex -C 조합이나 Worktrunk가 더 예측 가능합니다.
Q. Worktrunk로 만든 worktree 안에서 claude --worktree를 또 써도 되나요?
권하지 않습니다. worktree 안에 .claude/worktrees/…로 또 하나를 만드는 셈이라 구조가 복잡해집니다. Worktrunk를 쓸 때는 wt switch -c -x claude 브랜치로 Worktrunk가 만든 폴더에서 평범한 claude를 실행하세요. 그 세션은 worktree 안에서 돌고 있으니 파일 격리는 이미 되어 있습니다. 다만 공식 문서가 5장의 "본관 편집 차단" 검사가 적용된다고 밝힌 경우는 --worktree로 시작한 세션, EnterWorktree로 들어간 세션, 그런 세션을 재개한 경우입니다. 직접 만든 worktree에서 평범하게 시작한 세션에도 같은 차단이 걸리는지는 문서로 확인하지 못했으니, 권한 규칙으로 따로 선을 그어 두는 편이 안전합니다.
Q. wt merge가 main에 바로 병합해 버리면 리뷰는 언제 하나요?wt merge 전에 합니다. wt list로 어느 worktree가 앞서 있는지 보고, 7편(기존 시리즈)에서 익힌 git diff main...브랜치로 전체 변경을 읽은 다음 병합하세요. pre-merge 훅에 테스트를 걸어 두면 테스트가 실패할 때 병합이 멈춥니다. 팀 저장소라면 wt merge 대신 push 후 PR을 여는 쪽이 맞습니다.
이번 편 요약
주제
기억할 것
도구가 해 주는 일
만들기 · 준비하기 · 치우기. 도구마다 강한 단계가 다르다
Claude Code
-w 이름 → .claude/worktrees/이름 + worktree-이름 · 기본 출발점은 origin 기본 브랜치 · "#1234"로 PR · 본관 편집·명령·git 리다이렉트 차단 · 깨끗하면 자동 삭제, 작업 있으면 질문
.worktreeinclude
gitignore 문법, 무시됨 ∩ 일치만 복사 · Claude Code 전부 + Codex 앱 · WorktreeCreate 훅이면 처리 안 됨
서브에이전트
isolation: worktree · 변경 없으면 자동 삭제 · 진행 중 작업 위라면 baseRef: "head"
Codex
앱: 관리형 worktree(detached, 15개 유지, 스냅숏, .worktreeinclude) · CLI: --worktree는 있지만 문서 미기재·자동 정리 없음 → -C 조합도 좋음
Worktrunk
wt switch -c -x claude · 훅(pre-start/post-start) · reflink 복사 · hash_port · wt list · wt merge · wt remove
다음 편 예고
지금까지는 한 대의 Mac 안에서 방을 나누는 이야기였습니다. 그런데 민지는 낮에는 맥북을 들고 카페에 가고, 저녁에는 사무실의 맥 스튜디오에서 에이전트를 돌립니다. 맥북의 worktree에서 하던 일을 맥 스튜디오에서 이어 가려면 어떻게 해야 할까요? 폴더를 동기화하면 안 된다는 것은 1편에서 봤습니다. 4편 「Mac을 바꿔 가며 이어서 작업하기」에서는 WIP 커밋과 자주 push하기, 브랜치 이름 규칙, 다른 Mac에서 fetch+switch로 이어받는 법, stash가 Mac을 건너지 못하는 이유를 다룹니다.