체크아웃은 분리됐는데 Git 상태는 공유된다: Codex 0.154 worktree의 세 경계

1. --worktree가 해결하는 충돌은 무엇인가

에이전트 두 개가 같은 저장소에서 동시에 파일을 고치면 가장 먼저 부딪히는 것은 모델이 아니다. 한쪽이 바꾼 작업 트리와 인덱스를 다른 쪽이 읽고, 테스트 산출물이 섞이고, 아직 정리하지 않은 변경을 다음 작업이 자기 입력으로 오해한다.

OpenAI는 GitHub 기준 2026년 9월 9일 22:35 UTC, 한국 시각 10일 07:35에 공개한 Codex 0.154.0에 실험적 managed worktree 기능을 넣었다. 새 실행이나 기존 세션의 fork를 --worktree로 시작할 수 있고, TUI에서는 /worktree로 새 대화 또는 현재 대화의 fork를 별도 체크아웃에 둘 수 있다. 공식 릴리스 설명은 이를 "isolated checkouts"라고 부른다. 다만 이 표현을 저장소 전체와 외부 환경까지 격리한다는 뜻으로 읽으면 곤란하다.

Git worktree가 분리하는 것은 체크아웃의 파일과 작업별 관리 상태다. Git 공식 문서에 따르면 연결된 worktree는 HEAD, index 같은 파일을 따로 갖지만 나머지 저장소 데이터는 공유한다. 따라서 같은 소스 파일을 동시에 편집하는 충돌은 줄일 수 있어도, 같은 원격 브랜치에 push하거나 같은 배포·테스트 데이터베이스를 건드리는 충돌까지 사라지지는 않는다.

원본 저장소에서 분리 체크아웃을 만들고 Codex thread 소유권을 결합한 뒤 목록에서 재개하는 네 단계

그림 1. Codex 0.154 managed worktree는 체크아웃 생성과 thread 소유권을 연결한다. binding 전 실패 시 체크아웃을 보존한다. 출처: github.com/openai/codex/releases/tag/rust-v0.154.0.

2. Codex는 새 체크아웃을 어떻게 만든다

0.154.0 태그가 가리키는 커밋의 codex-rs/worktree/src/lib.rs를 보면 생성 순서가 꽤 보수적이다. 먼저 현재 HEAD를 commit SHA로 확정한다. 그 SHA에서 git worktree add --detach --no-checkout을 실행한 뒤, 새 위치의 config.worktreecore.worktree를 기록하고 reset --hard --no-recurse-submodules로 파일을 만든다.

여기서 두 가지가 눈에 띈다. 새 worktree는 특정 브랜치가 아니라 detached HEAD에서 시작한다. 결과를 브랜치로 보존하려면 커밋 전에 목적에 맞는 브랜치를 명시적으로 만들어야 한다. 또 초기 파일 전개 과정에서는 저장소가 설정한 clean·smudge·process filter와 hooks, fsmonitor를 끈다. Git LFS smudge도 건너뛴다. 저장소가 제공하는 helper를 체크아웃 준비 단계에서 곧바로 실행하지 않으려는 선택이다.

이는 "모든 저장소 동작에서 hook과 filter가 영원히 꺼진다"는 뜻은 아니다. 확인한 코드는 managed worktree를 만들고 채우는 내부 Git 명령의 안전 경계다. 새 세션이 시작된 뒤 에이전트가 실행하는 일반 Git 명령까지 같은 설정이라고 확대해서는 안 된다.

Git worktree별로 분리되는 체크아웃 파일과 HEAD, index를 공유되는 object database, refs, repository config와 비교한 표

그림 2. linked worktree는 HEAD와 index를 따로 두지만 저장소의 object와 대부분의 ref, 기본 config를 공유한다. 출처: git-scm.com/docs/git-worktree/2.54.0.

3. 파일보다 어려운 것은 설정과 신뢰의 경계다

별도 디렉터리만 만든다고 안전한 세션이 되는 것은 아니다. 새 체크아웃에는 원래 경로와 다른 프로젝트 설정이나 지시 파일이 나타날 수 있다. 0.154.0에 포함된 PR #43069는 그래서 목적지 설정을 telemetry와 login policy 초기화 전에 읽고, 첫 turn 전에 체크아웃 소유권을 새 thread에 묶는다. 원본이나 목적지가 명시적으로 신뢰되지 않은 상태라면 worktree 생성을 거부한다.

codex-thread.json은 화면 라벨보다 더 구체적인 소유권 기록이다. Codex는 이 파일을 worktree의 Git 관리 경로에 원자적으로 만들고, 이미 다른 thread ID가 기록돼 있으면 덮어쓰지 않는다. 같은 thread를 다시 묶는 동작만 허용한다. /worktree 브라우저는 이 기록을 읽어 소유 세션을 재개하거나, 소유 메타데이터가 없을 때 경로를 복사하는 선택지를 제공한다.

이 구조는 체크아웃과 대화의 관계를 명시하지만 OS 프로세스 락은 아니다. 다른 터미널이나 별도 도구가 그 디렉터리에 직접 들어가는 것을 막아 주지 않는다. 운영 규칙에서 "worktree 하나당 실행 주체 하나"를 따로 지켜야 하는 이유다.

원본과 실행 환경 확인, managed checkout 생성, 목적지 설정 재해석, thread ownership 결합의 시작 순서

그림 3. 새 checkout의 설정과 신뢰 정책을 첫 turn 전에 다시 읽고, 그 뒤 thread 소유권을 원자적으로 결합한다. 출처: github.com/openai/codex/pull/43069.

4. 공유되는 Git 상태를 놓치면 다시 충돌한다

Git 공식 문서는 연결된 worktree가 "같은 저장소에 붙어 있다"고 설명한다. 보통 브랜치 ref와 object database, repository config는 공통 영역을 쓴다. 반면 HEAD, index, worktree별 ref 일부는 각 worktree의 관리 디렉터리에 있다. extensions.worktreeConfig를 켜지 않으면 저장소 config도 기본적으로 공유된다.

이 차이는 실무에서 바로 드러난다.

  • 두 세션이 서로 다른 파일을 고쳐도 같은 브랜치 이름을 갱신하려 하면 ref 수준에서 만난다.
  • 한 세션이 git config로 공통 설정을 바꾸면 다른 worktree에도 영향을 줄 수 있다. worktree 전용 설정이 필요하면 Git의 extensions.worktreeConfig 계약을 따로 검토해야 한다.
  • Git 디렉터리가 갈라져 보여도 object와 ref 경로를 문자열로 추측하면 틀리기 쉽다. Git 문서는 내부 경로가 필요할 때 git rev-parse --git-path를 쓰라고 권한다.
  • 서브모듈 지원은 Git 문서에서도 불완전하다고 경고한다. Codex의 초기 reset 역시 --no-recurse-submodules를 사용한다.

따라서 worktree는 파일 편집 충돌을 줄이는 도구이지, 저장소 복제나 컨테이너 샌드박스의 대체물이 아니다. 의존성 캐시, 환경 변수, 포트, 로컬 DB, 클라우드 계정은 별도 자원 이름과 락이 필요하다.

5. 실제 운영에서는 세 경계를 따로 기록한다

첫째는 파일 경계다. 각 실행의 worktree 경로와 시작 commit SHA를 남기고, 산출물이 그 경로 밖의 공용 디렉터리에 쓰이지 않는지 본다. 둘째는 Git 경계다. detached HEAD에서 무엇을 보존할지, 어느 브랜치와 원격 ref를 누가 갱신할지 정한다. 셋째는 외부 효과 경계다. 배포, 이슈 작성, 데이터 변경처럼 Git과 무관한 쓰기는 안정적인 ID와 원격 대조가 필요하다.

정리도 자동이라고 가정하면 안 된다. 0.154.0 소스에서 CLI용 설정은 Desktop과 worktree root를 공유하지만 auto_cleanup_enabled를 false로 둔다. TUI 시작이 thread binding 전에 실패하면 체크아웃을 남기고, 상태를 확인한 뒤 원본 저장소에서 git worktree remove <path>를 실행하라는 복구 문구를 출력한다. 릴리스가 "실험적"이라고 부르는 이유 중 하나가 이 수명주기다.

이번 글은 Codex 0.154.0의 공개 릴리스, 기능 PR 네 개, 고정 태그 소스, Git 2.54.0 문서를 대조했다. 실제 대규모 병렬 에이전트의 처리량이나 충돌률을 측정한 결과는 아니다. 이번 범위에서 가져갈 결론은 이 정도다. worktree를 켠 뒤에도 "무엇이 분리되고 무엇이 공유되는가"를 적지 않으면, 충돌은 파일 밖에서 다시 나타난다.

참고 자료

독자가 확인할 수 있는 작은 Git 대조 실험

2026-09-12 보강: 별도의 임시 저장소에서 Git 2.50.1 (Apple Git-155)로 파일·인덱스·기본 설정·object 경로를 확인했습니다. 기본 설정을 바꾸지 않은 linked worktree가 대상입니다. Codex 0.154 자체를 실행한 시험이나 Git 2.54 실행 결과가 아닙니다.

# 새 임시 디렉터리에서만 실행: 기존 저장소에는 실행하지 마세요.
mkdir main && cd main
git init -q
git -c user.name=Example -c user.email=example@example.invalid commit --allow-empty -qm base
printf 'base\n' > sample.txt
git add sample.txt
git -c user.name=Example -c user.email=example@example.invalid commit -qm sample
git worktree add --detach ../linked
printf 'linked edit\n' > ../linked/sample.txt
git -C ../linked add sample.txt
git diff --cached --name-only
git -C ../linked diff --cached --name-only
git config vpl.example shared
git -C ../linked config vpl.example
git rev-parse --path-format=absolute --git-path objects
git -C ../linked rev-parse --path-format=absolute --git-path objects

이번 로컬 확인에서 원본 sample.txt는 base 그대로였고, 원본 인덱스의 변경 목록은 비어 있었습니다. linked 인덱스에는 sample.txt가 나왔습니다. linked에서 읽은 vpl.example은 shared였고 두 object 경로는 같았습니다. 임시 위치를 사용하므로 절대 경로 문자열은 실행마다 다릅니다. 위 순서로 각각 독립 여부를 확인할 수 있습니다.

이 대조가 검증하는 것은 일반 Git의 분리·공유 경계뿐입니다. Codex의 신뢰 검사·thread binding·cleanup 동작은 앞서 연결한 고정 소스 검토에 근거합니다. Git 2.54.0 공식 문서 원문의 HEAD·index 분리 설명과 대조했습니다. 임시 디렉터리는 확인 뒤 삭제할 수 있으며 실제 프로젝트의 worktree를 삭제하지 마세요.

댓글

이 블로그의 인기 게시물

잠근 작업이 커밋 뒤 다시 나온다: PostgreSQL 에이전트 큐의 소유권 설계

코딩 에이전트는 API 문서보다 AGENTS.md를 먼저 읽었다