본문으로 건너뛰기

© 2026 Molayo

Qiita헤드라인2026. 06. 23. 01:39

worktree로 격리했다고 생각했는데 편집이 전부 main에 반영되었다——Claude Code의 조용한 데이터 소실(이슈 #70069)

요약

Claude Code의 git worktree 사용 시, 작업 내용이 격리되지 않고 메인 브랜치에 직접 반영되는 데이터 소실 이슈를 다룹니다. 모델이 상대 경로를 절대 경로로 변환할 때 worktree 루트가 아닌 리포지토리 루트를 기준으로 삼는 것이 원인입니다.

핵심 포인트

  • Claude Code worktree 세션에서 편집 내용이 메인 브랜치로 유출되는 버그 보고
  • 원인: 모델이 경로 변환 시 worktree 루트가 아닌 리포지토리 루트를 참조함
  • 에러나 경고 없이 정상적으로 메인 파일이 수정되어 인지가 어려움
  • 방지책: 편집 대상 경로가 worktree 루트 내에 있는지 문자열 비교로 검증 가능

git worktree는 메인 흐름(main)을 더럽히지 않고 별도의 브랜치 작업을 물리적으로 분리하기 위한 메커니즘이다. 병행하여 기능 개발을 진행하는 사람이나, AI 에이전트에게 작업을 맡기는 사람일수록 "worktree에 격리해 두면 메인 흐름은 안전하다"라고 생각한다. claude -w <이름>

으로 worktree 세션을 열면, 편집은 그 worktree 내부에서만 이루어진다——고 생각했다.

하지만 2026년 6월 22일에 올라온 이슈(issue) #70069에서는, worktree에서 작업하고 있었다고 생각했는데 약 70건의 편집이 모두 메인 흐름(master) 측 파일에 반영되었다는 보고가 있다. worktree의 브랜치는 거의 빈 상태로 남았고, 실제 성과는 전부 main에 들어가 있었다고 한다. 게다가 작업 중 경고는 단 한 번도 발생하지 않았다고 한다.

필자는 이 사고를 직접 겪지는 않았으므로, 이슈 내용은 전언으로서 소개한다. 다만, 이용자 측에서 미리 방지할 방법은 실제로 직접 구성하고 실행하여 확인했으므로, 그 부분은 단정적으로 기술한다.

보고에 따르면, 세션 기록(transcript)은 마지막까지 정확했다고 한다.

  • 작업 디렉토리(cwd)는 <리포지토리>/.claude/worktrees/<이름>을 가리키고 있었다.
  • 브랜치(gitBranch)도 worktree-<이름>을 가리키고 있었다.

즉, 하네스(harness)는 "자신이 worktree 안에 있다"라고 올바르게 인식하고 있었다. 그럼에도 불구하고, 약 70회의 Edit/Write1건을 제외한 전부가 worktree가 아닌 메인 흐름의 체크아웃(<리포지토리>/packages/...)의 절대 경로를 수정하고 있었다고 한다.

이슈에서 제시하는 원인은 다음과 같다. 모델은 메모(auto-memory) 등에 적힌 "리포지토리 루트로부터의 상대 경로" (예: packages/rrweb/src/record/foo.ts)를 절대 경로로 변환할 때, worktree의 루트가 아닌 리포지토리의 루트에 연결했다.

이 부분이 까다로운데, worktree는 리포지토리의 내부.claude/worktrees/<이름>에 존재한다. 따라서 <리포지토리>/packages/...라는 절대 경로는 "존재하는 올바른 파일"이다. 다만 복사본의 차이——즉, 메인 흐름 측의 파일이다. 읽기와 편집 모두 정상적으로 성공하기 때문에 에러도 경고도 발생하지 않는다. worktree 내부의 동일한 상대 위치에 있는 파일에는 단 한 번도 닿지 않은 채 종료된다.

결과적으로 격리하려던 작업이 메인 흐름에 직접 쓰여지고, 그대로 커밋된다. git 이력으로 추적할 수는 있겠지만, 그 메인 흐름 브랜치가 나중에 rebaseforce-push로 덮어씌워진다면 작업은 통째로 소실된다. "격리해 두었다"라는 확신이 있는 만큼, 알아차리는 것이 늦어진다. 데이터 소실과 같은 유형의 사고다.

이 사고의 긍정적인 점(이라고 말할 수 있다면)은, 판정을 결정론적(deterministic)으로 작성할 수 있다는 것이다. 확률적인 분류기에 의존하지 않고도, 문자열 비교만으로 "편집이 worktree를 탈출하여 메인 흐름을 가리키고 있지 않은가"를 확실하게 판정할 수 있다.

생각하는 방식은 다음과 같다.

  • 세션의 cwd.claude/worktrees/<이름>을 포함한다면, worktree 세션이라고 판정한다.
  • cwd로부터 리포지토리의 루트와 worktree의 루트를 추출한다.
  • 편집 대상의 절대 경로가 리포지토리의 루트 아래에 있지만, worktree의 루트 아래에는 없다면, 그것은 메인 흐름 측의 복사본을 가리키고 있다 $\rightarrow$ 차단한다.

상대 경로는 cwd(=worktree 내부)를 기준으로 해결되므로 안전하며, 리포지토리 외부를 가리키는 절대 경로는 이 가드(guard)의 담당 외(별도의 가드가 확인)로 구분하면 오탐(false positive)이 적게 발생한다.

PreToolUse 후크로서, Edit/Write/MultiEdit를 대상으로 구현한 예시는 다음과 같다. 이는 실제로 직접 실행하여 16가지 입력(탈출하는 편집은 차단, worktree 내부·상대 경로·worktree 외부의 세션·리포지토리 외부의 경로·대상 외의 도구는 모두 통과)에 대해 기대한 대로 동작함을 확인했다.

#!/bin/bash
# worktree-escape-write-guard.sh
# worktree 세션 중에 편집이 메인 흐름의 체크아웃으로 탈출하는 것을 방지함
...

exit 2

exit 2를 반환하면, Claude Code는 해당 도구의 실행을 중단하고 표준 에러(standard error)로 출력된 메시지를 모델에게 다시 전달한다. 따라서 모델은 "경로가 worktree를 탈출했다"는 이유를 인지하고, worktree의 루트 하위에 다시 작성할 수 있게 된다. cwdtool_input.file_path 모두 PreToolUse 훅(hook)의 입력값으로 그대로 들어오기 때문에, 외부 상태에 의존하지 않고도 판정이 가능하다.

"작업 디렉터리(working directory)가 프로젝트 외부로 나가면 경고한다"와 같은 형태의 가드는 이전부터 존재해 왔다. 하지만 이번 사고는 그런 방식으로는 잡아낼 수 없다. cwd는 마지막까지 올바르게 worktree 내부에 머물러 있었기 때문이다. 탈출하고 있는 것은 cwd가 아니라, 개별 편집의 file_path 쪽이다. 따라서 확인해야 할 대상이 다르다.

PreToolUse에서 file_path를 확인하는 것이 올바른 방법이다.

  • worktree는 격리 메커니즘이지만, 상대 경로(relative path)의 해결(resolution)이 리포지토리의 루트에 묶여 있으면, 편집 내용이 메인 흐름의 복사본에 조용히 안착할 수 있다 (이슈 #70069 보고)
  • cwd가 올바르더라도 발생하므로, cwd 모니터링만으로는 막을 수 없다. PreToolUse에서 file_path가 worktree를 탈출했는지 확인해야 한다. 판정은 문자열 비교만으로 결정론적(deterministic)으로 작성할 수 있으며, 오탐(false positive)도 억제할 수 있다.

worktree를 사용하는 병행 개발이나 AI 에이전트 운용에서는, 격리되어 있다는 확신이 있는 만큼 사고를 인지하는 것이 늦어진다. 로컬에서 실행할 수 있는 한 줄의 가드로 미리 대비해 두면, 메인 흐름의 오염과 그 이후의 rebase / force-push 과정에서 발생하는 데이터 소실을 미연에 방지할 수 있다.

이러한 종류의 "설정으로 미리 방지할 수 있는 데이터 소실 및 파괴 사고"를 한 권에 정리했습니다. /rewind를 통한 코드 소실, 운영 DB 삭제, git stash / worktree / submodule deinit의 함정, Windows 특유의 소실, 클라우드 자원 삭제까지, 모두 실제 기기에서 검증한 방지법을 총 43장에 걸쳐 다루고 있습니다.

  • Zenn 버전: Anthropic 공식 가이드에 없는 사고 방지——Claude Code 800+시간을 통해 19점에서 85점으로 올린 모든 기록 (¥800) (3장까지 무료)
  • Kindle 버전 (¥800 · Kindle Unlimited 대응): Amazon에서 읽기
  • 무료 훅 모음 (약 900개 · MIT 라이선스): cc-safe-setup — npx cc-safe-setup --shield로 일괄 도입할 수 있습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0