LabHub
배우기 러닝패스 코스

Git in Practice

Add Another Desk Instead of Stashing Your Work

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

git worktree 는 저장소 하나(.git 하나)에 작업 디렉터리를 여러 개 붙이는 기능입니다. 커밋·브랜치·객체는 전부 공유하고 체크아웃한 자리만 달라집니다. 그래서 하던 일을 어디로도 밀어 넣지 않은 채 다른 브랜치를 열 수 있습니다.

Concept map: 작업 디렉터리를 여러 개 · 디렉터리가 아니라 파일 · 관리 파일만

왜 이게 필요했나

금요일 오후에 이런 일이 납니다. 기능 브랜치에서 파일 열두 개를 반쯤 고쳐 놓았는데, 운영에서 장애가 나고 릴리스 브랜치에 핫픽스를 넣어야 합니다. 보통은 이렇게 합니다.

git stash
git switch release
# 고치고 커밋하고 올리고
git switch feature
git stash pop

이 흐름에는 함정이 셋 있습니다. 첫째, git stash추적되지 않는 파일을 기본으로 담지 않습니다. 새로 만든 파일은 브랜치를 바꿔도 그 자리에 남아 있다가 엉뚱한 브랜치에 섞입니다. 둘째, stash 는 이름이 없는 스택이라 두세 개가 쌓이면 무엇이 무엇인지 알 수 없고, stash pop 이 충돌하면 그 자리에서 복구를 해야 합니다. 셋째, 브랜치를 바꾸는 순간 빌드 산출물과 의존성 디렉터리가 통째로 무효가 됩니다. 큰 프로젝트에서는 이 재빌드가 핫픽스 자체보다 오래 걸립니다.

두 번째 사본을 git clone 으로 만드는 방법도 있습니다. 그러면 위 세 가지는 해결되지만 이력을 통째로 한 벌 더 받아야 하고, 두 사본의 브랜치가 따로 놀기 시작합니다. 한쪽에서 만든 커밋이 다른 쪽에 없어서 결국 둘 사이에 또 push 와 fetch 를 합니다.

git worktree 는 그 가운데를 정확히 메웁니다. 객체 저장소는 하나라 이력을 다시 받지 않고, 디렉터리는 둘이라 빌드 산출물이 섞이지 않습니다.

어떻게 동작하나

git worktree add <경로> <브랜치> 를 하면 두 가지가 생깁니다.

여기서 중요한 사실이 하나 나옵니다. HEAD 와 인덱스는 작업 사본마다 따로지만, 브랜치 참조(refs/heads/*)는 저장소 전체가 공유합니다. 그래서 같은 브랜치를 두 자리에서 체크아웃하려고 하면 git 이 거절합니다.

$ git worktree add /tmp/dup main
fatal: 'main' is already used by worktree at '/home/me/repo'

이것은 불친절이 아니라 보호입니다. 브랜치 참조가 하나인데 작업 트리가 둘이면, 한쪽에서 커밋하는 순간 다른 쪽은 자기가 만들지 않은 커밋 위에 앉게 되고 인덱스와 실제 파일이 어긋납니다. 정말 같은 커밋을 두 자리에서 보고 싶다면 --detach 로 브랜치 없이 붙입니다.

정리는 세 가지 명령으로 갈립니다.

상황 명령 하는 일
작업이 끝나 깔끔히 접는다 git worktree remove <경로> 디렉터리와 관리 파일을 함께 지운다
디렉터리를 이미 rm -rf 했다 git worktree prune 남은 관리 파일만 지운다
외장 디스크처럼 잠시 안 보인다 git worktree lock prune 이 건드리지 못하게 잠근다

rm -rf 로 디렉터리만 지우면 git worktree list 에 그 자리가 prunable 이라고 계속 남습니다. 그 상태에서 같은 이름으로 다시 add 하면 "이미 있다" 는 소리를 듣습니다. 그리고 prune관리 파일만 지웁니다 — 그 자리에서 만들었던 브랜치는 그대로 남으므로, 브랜치까지 정리하려면 git branch -d 를 따로 해야 합니다.

현장에서 만나는 모습

주의할 것도 있습니다. 작업 사본 안에서 git gc 가 돌 때 다른 작업 사본이 참조하는 객체를 살려 두려면 git 이 그 자리들을 알아야 하는데, rm -rf 로 지워 놓고 prune 을 안 한 자리가 있으면 그 정보가 낡습니다. 정리를 미루지 않는 편이 안전합니다.

다음 실습에서 할 것

작업 사본 셋을 붙여 놓고, 같은 브랜치를 두 번 열려다 거절당하는 것을 직접 봅니다. 저장하지 않은 변경을 그대로 둔 채 핫픽스를 따로 커밋하고, 디렉터리를 rm -rf 했을 때 prunable 이 뜨는 것과 prune 뒤에도 브랜치는 남는 것을 확인합니다. 마지막으로 잠긴 작업 사본이 remove 를 어떻게 거절하는지까지 봅니다.

공식 문서는 git-worktree 입니다.