git worktree lock으로 Claude Code 워크트리 자동 삭제 방지하기

Published · AI Daily — AI-assisted deep research, methodology & disclosure

이 글은 Claude Code (v2.1.263)가 `--worktree` 플래그로 생성한 워크트리를 종료 시 자동 삭제하는 내부 로직을 분석합니다. 변경 사항이나 새 커밋 없이 세션이 종료되면 워크트리와 브랜치가 경고 없이 삭제됩니다. 번들된 소스 코드를 분석하여 삭제가 발생하는 정확한 조건을 밝히고, Claude Code가 특정 형식의 `git worktree lock`을 사용하여 자체 세션을 추적하는 방법을 설명합니다. 이 형식과 일치하지 않는 이유로 잠긴 워크트리는 외부 잠금으로 간주되어 삭제되지 않습니다. 수동으로 잠금 해제 후 재잠금하거나 `SessionStart` 훅을 통해 자동화하는 실용적인 솔루션을 제공합니다.

배경 및 해결 과제

Claude Code는 `--worktree` 모드를 통해 독립적인 Git worktree에서 세션을 실행할 수 있게 해준다. 이를 활용하면 메인 작업 디렉토리를 오염시키지 않고 코드 조사나 실험적 수정을 진행할 수 있다. 그러나 `claude --worktree feature-x`로 세션을 생성한 뒤 코드를 살펴보거나 수정했다가 되돌린 후 `/exit`로 종료하면, worktree와 연결된 브랜치가 아무런 확인 없이 조용히 사라진다. 많은 개발자가 "변경 사항이 없으면 안전할 것"이라고 생각하지만, 실제로는 작업 컨텍스트 전체가 증발해 버린다.

공식 문서는 이를 설계적 특성으로 명시하고 있다: 커밋되지 않은 변경 사항이 없고 세션에 이름을 부여하지 않으면 worktree와 브랜치가 자동 제거되며, 이름을 붙인 세션에서는 "Keep/Remove" 선택 대화상자가 나타난다. 하지만 이 동작을 전역적으로 비활성화하는 구성 옵션은 존재하지 않는다. 따라서 "읽기만 하고 쓰지 않음" 또는 "수정 후 되돌림"과 같은 임시 조사 시나리오는 모두 worktree가 무음 삭제될 위험에 노출된다. GitHub 이슈(#27753, #46444, #58432 등)에서도 이 문제가 반복적으로 제기되었지만, 모두 "의도된 동작"으로 종결되어 사용자 커뮤니티의 불만이 쌓여 왔다.

아키텍처 및 구현 메커니즘

Claude Code의 정리 로직은 번들된 JavaScript 소스에 숨겨져 있다. 분석 결과, `/exit` 시점에 두 가지 Git 상태 지표를 순차적으로 확인한다. 첫째, `git status --porcelain`을 실행하여 스테이징되지 않은 변경이나 추적되지 않은 파일이 있는지 검사한다. 둘째, `git rev-list --count <session-start-HEAD>..HEAD`로 세션 시작 시점의 HEAD 커밋 이후 새로 생성된 커밋 수를 센다. 이 두 값의 조합에 따라 동작이 세 가지로 분기된다: (1) 두 값 모두 0이고 세션이 이름 없음 → 무음 삭제; (2) 두 값 모두 0이지만 세션에 이름 있음 → Keep/Remove 대화상자 표시; (3) 하나라도 0이 아님 → 이름 여부와 무관하게 대화상자 표시.

문제의 핵심은 첫 번째 경우다. "조사 후 아무 변경 없음" 또는 "변경 후 되돌림"과 같은 전형적인 사용 패턴에서 정확히 이 조건이 충족된다. 더욱 흥미로운 점은 Claude Code 자체가 `git worktree lock`을 사용해 세션 잠금을 관리한다는 사실이다. 세션 시작 시 일정한 형식의 잠금 이유를 작성한다: `claude session <name> (pid <PID> start <timestamp>)`. 종료 시 코드는 정규식 `/^claude (?:agent|session) .{1,255} \( pid (\d{1,10})(?: start (.{1,255}))? \)$/`을 사용해 잠금이 자신의 것인지 검증한다. 만약 잠금 이유가 이 패턴과 일치하지 않으면(예: 사용자 정의 텍스트), "다른 프로세스에 의해 잠겼다"고 판단하고 정리를 건너뛴다. 이는 Git 자체가 제공하는 안전망이다. 공식 문서에서도 "sweep은 사용자가 직접 설정한 잠금을 절대 해제하지 않는다"고 설명한다.

실측 벤치마크 및 엔지니어링 가치

테스트 저장소에서 정확히 재현할 수 있다. `claude --worktree wt-clean`으로 빈 worktree에 진입한 후 바로 `/exit`를 입력하면, 터미널에 `Cleaning up worktree (no pending changes)…` 메시지가 스치듯 나타나고 worktree 디렉터리와 브랜치가 즉시 사라진다. `git worktree list`를 실행하면 메인 저장소의 worktree만 남아 있음을 확인할 수 있다. 이 과정은 1초도 걸리지 않으며 되돌릴 기회가 전혀 없다.

보호 방안은 앞서 설명한 잠금 메커니즘을 활용한다. 세션이 실행 중인 동안 다른 터미널을 열거나 Claude Code 내에서 `!` 명령어로 셸을 실행해, 먼저 `git worktree unlock .claude/worktrees/<name>`으로 기존 잠금을 해제한 후, `git worktree lock --reason "pinned by ryan" .claude/worktrees/<name>`으로 사용자 정의 이유로 다시 잠근다. 반드시 unlock → lock 순서를 따라야 한다. 기존 잠금이 존재하는 상태에서 lock을 시도하면 오류가 발생하기 때문이다. 재잠금 후 `/exit`를 실행하면 잠금 이유가 정규식과 일치하지 않아 Claude Code가 삭제를 건너뛴다. `git worktree list`에서 worktree가 여전히 `locked` 상태로 남아 있음을 확인할 수 있으며, 이후 `claude --worktree wt-lock`으로 다시 세션을 열어 정상적으로 재사용할 수 있다.

자동화를 원한다면 프로젝트의 `.claude/hooks/` 디렉터리에 `SessionStart` 훅 스크립트(예: `pin-worktree.sh`)를 작성한다. 이 스크립트는 `git rev-parse --git-dir`과 `--git-common-dir`을 사용해 현재 디렉터리가 linked worktree인지(메인 저장소는 제외) 판단한 후, unlock과 lock을 순차 실행한다. 이렇게 하면 세션이 시작될 때마다 자동으로 잠금이 적용되고, 종료 시 삭제가 면제되므로 수동 개입이 필요 없다.

향후 전망 및 시사점

이 문제의 본질은 AI 지원 코딩 도구가 "자동 정리" 기능을 설계할 때 사용자의 멘탈 모델과 실제 작업 흐름을 충분히 반영하지 못했다는 점이다. 개발자는 `--worktree`를 독립적인 개발 컨테이너로 인식하며, Git 기록에 흔적이 남지 않더라도 해당 worktree가 제공하는 조사 환경, 터미널 히스토리, 대화 컨텍스트에 가치를 둔다. Claude Code는 "변경 없음"을 유일한 판단 기준으로 삼았지만, worktree가 환경 컨테이너로서 갖는 존재 의미를 간과했다.

엔지니어링 관점에서 Claude Code의 잠금 메커니즘은 커뮤니티에 유연한 방어 인터페이스를 제공한다. 공식 설정 스위치는 없지만, Git 네이티브 기능을 활용해 동등한 효과를 구현할 수 있었다. 이는 훌륭한 도구 설계가 "자동화의 편리함"과 "사용자 통제감" 사이에 사용자 정의 공간을 남겨두어야 함을 시사한다. 향후 버전에서 `--keep-worktree`와 같은 명시적 플래그나 설정 파일에 `worktreeCleanupPolicy` 옵션이 도입된다면 근본적으로 해결될 것이다. 또한 커뮤니티에서 제안된 `WorktreeRemove` 훅도 주목할 만하다. 이 훅을 통해 정리 전 사용자 정의 검사를 실행할 수 있어 안전성을 한층 높일 수 있다.

Claude Code를 깊이 활용하는 팀이라면 worktree 잠금 스크립트를 프로젝트 초기화 템플릿에 포함시키는 것을 표준 관행으로 삼길 권장한다. 이는 임시 작업 영역을 보호할 뿐만 아니라, 실수로 브랜치가 사라져 협업에 혼란을 초래하는 사고를 방지한다. 기술적 세부 사항의 작은 개선이 팀 전체의 개발 효율성을 크게 향상시킬 수 있다.

Sources

FAQ

Claude Code의 worktree 자동 정리 메커니즘은 어떻게 트리거되나요?

--worktree 모드로 임시 조사 후 변경 사항이나 새 커밋이 없고 세션 이름 없이 종료하면 worktree와 브랜치가 자동으로 삭제됩니다.

worktree 삭제를 방지하려면 어떻게 해야 하나요?

`git worktree lock`을 사용하여 잠그거나 SessionStart 훅을 이용해 자동으로 unlock 후 lock을 수행하여 영구 보존할 수 있습니다.

자동 정리를 비활성화하는 설정 옵션이 있나요?

공식 문서에서는 설계상의 기능으로 인정하며 설정 스위치가 없어 외부 방어(수동 잠금 등)만 가능합니다.