환경  Git 2.50.1 (Apple Git-155) · macOS · 2026-08-09 확인

급한 요청은 늘 무언가를 반쯤 고쳐 놓은 상태에서 들어옵니다. 지금 브랜치에는 커밋하기 애매한 변경이 널려 있는데, 다른 브랜치의 버그를 지금 봐야 합니다. 흔한 대응은 stash 로 밀어 두고 브랜치를 옮겼다가 돌아와 되살리는 것인데, 이 왕복이 반복되면 어느 변경이 어느 stash 에 들어갔는지 헷갈리고 빌드 산출물도 브랜치마다 다시 만들어야 합니다. Git 에는 저장소 하나에 작업 폴더를 여러 개 두는 기능이 따로 있습니다. 폴더를 나누면 브랜치를 갈아타지 않아도 되고, 편집하던 상태는 건드릴 일이 없습니다.

하던 작업을 그대로 두고 다른 브랜치를 열어야 할 때

만들려는 것은 간단합니다. 지금 쓰고 있는 저장소 폴더는 그대로 두고, 그 옆에 같은 저장소를 바라보는 작업 폴더를 하나 더 만드는 것입니다. 새 폴더에는 다른 브랜치가 체크아웃되어 있고, 두 폴더는 같은 커밋·같은 브랜치 목록을 공유합니다. 편집기 창을 두 개 띄워 두면 브랜치를 오가는 동작 자체가 사라집니다.

이 방법이 필요한 이유는 브랜치 전환이 항상 되는 동작이 아니기 때문입니다. 지금 고치고 있는 파일이 상대 브랜치에서도 달라진 파일이면 Git 은 전환을 거부합니다. 아래는 src/index.js 를 편집 중인 상태에서 다른 브랜치로 옮기려 했을 때 실제로 돌아온 응답입니다. 커밋하거나 stash 하라는 안내와 함께 종료 코드 1 로 끝납니다.

stash 로 우회하는 것이 틀린 방법은 아니지만, 되돌릴 때가 문제입니다. stash 는 브랜치에 매이지 않아서 다른 브랜치에서 pop 해도 막지 않고, 여러 개가 쌓이면 순서를 사람이 기억해야 합니다. 무엇보다 폴더가 하나뿐이라 node_modules 나 빌드 산출물이 브랜치를 오갈 때마다 상태가 어긋납니다. 작업 폴더를 나누면 이 문제들이 함께 사라집니다.

git status --short
#  M src/index.js

git switch feature/api
# error: Your local changes to the following files would be overwritten by checkout:
# 	src/index.js
# Please commit your changes or stash them before you switch branches.
# Aborting
# exit=1

편집 중인 파일이 상대 브랜치에서도 바뀐 파일이면 전환 자체가 막힙니다.

브랜치를 갈아타는 대신 폴더를 하나 더 두는 것이 이 절차의 전부입니다.

시작하기 전에 확인할 것

먼저 손에 있는 Git 이 어떤 하위 명령을 갖고 있는지 봅니다. worktree 명령은 오래전부터 있었지만 remove·move·repair 같은 하위 명령은 뒤에 추가된 것들이라, 저장소 서버나 CI 이미지에 담긴 오래된 Git 에서는 없을 수 있습니다. git worktree -h 를 한 번 찍어 보면 그 버전이 지원하는 목록이 그대로 나옵니다. 아래 출력과 이후의 모든 결과는 Git 2.50.1 에서 받은 것입니다.

다음으로 지금 열려 있는 작업 폴더 목록을 확인합니다. git worktree list 는 저장소가 알고 있는 모든 작업 폴더를 경로·커밋·브랜치 순으로 한 줄씩 보여 줍니다. 아직 아무것도 만들지 않았다면 본체 한 줄만 나옵니다. 남이 쓰던 저장소라면 이 목록에 모르는 경로가 남아 있을 수 있으니, 만들기 전에 한 번 보는 편이 좋습니다.

마지막으로 새 폴더를 어디에 둘지 정합니다. 저장소 안에 만들 수도 있지만 권하지 않습니다. 저장소 폴더 밑에 만들어 보면 그 폴더가 미추적 항목으로 잡히고, git add . 한 번에 통째로 커밋될 자리에 놓입니다. 저장소와 나란한 형제 폴더에 두면 이 문제가 생기지 않습니다. 이름은 저장소 이름 뒤에 용도를 붙이는 식이 뒤에 정리할 때 편합니다.

git --version
# git version 2.50.1 (Apple Git-155)

git worktree -h
# usage: git worktree add [-f] [--detach] [--checkout] [--lock [--reason <string>]]
#                         [--orphan] [(-b | -B) <new-branch>] <path> [<commit-ish>]
#    or: git worktree list [-v | --porcelain [-z]]
#    ... lock / move / prune / remove / repair / unlock

git worktree list
# /Users/me/work/acme  beab590 [main]

# 저장소 안에 만들면 미추적 폴더로 잡힌다
git worktree add ./inside -b tmp/inside
git status --short
# ?? inside/

저장소 안쪽은 피합니다. 커밋에 딸려 들어갈 자리에 작업 폴더를 두게 됩니다.

새 폴더를 만들어 브랜치를 연다

만드는 명령은 한 줄입니다. git worktree add 뒤에 새 폴더 경로를 주고, -b 로 그 자리에서 만들 브랜치 이름을, 마지막에 어느 커밋에서 갈라져 나올지를 적습니다. 아래는 main 에서 hotfix/login 브랜치를 새로 만들며 형제 폴더 acme-hotfix 를 여는 예입니다. 준비 중이라는 안내와 체크아웃된 커밋이 찍히고 끝납니다.

만든 뒤 git worktree list 를 다시 보면 줄이 하나 늘어 있습니다. 그리고 원래 폴더로 돌아가 상태를 보면, 앞에서 전환을 막았던 src/index.js 의 변경이 손대지 않은 채 그대로 남아 있습니다. stash 로 밀어 둔 것이 아니라 애초에 건드리지 않았기 때문입니다. 이 시점부터 두 폴더는 각자의 브랜치에서 독립적으로 편집됩니다.

인자를 줄이는 방법도 있습니다. 공식 문서는 브랜치도 커밋도 지정하지 않고 경로만 주면 경로의 마지막 이름으로 브랜치를 새로 만들어 준다고 설명합니다. 실제로 acme-review 폴더만 지정하면 acme-review 브랜치가 생깁니다. 반대로 브랜치를 만들지 않고 특정 커밋의 상태만 잠깐 열어 보고 싶다면 --detach 를 씁니다. 리뷰나 이분 탐색처럼 커밋 하나만 확인하면 되는 경우에 맞습니다.

cd ~/work/acme
git worktree add ../acme-hotfix -b hotfix/login main
# Preparing worktree (new branch 'hotfix/login')
# HEAD is now at beab590 init

git worktree list
# /Users/me/work/acme         beab590 [main]
# /Users/me/work/acme-hotfix  beab590 [hotfix/login]

git status --short          # 원래 폴더는 편집하던 그대로다
#  M src/index.js

# 경로만 주면 폴더 이름으로 브랜치가 생긴다
git worktree add ../acme-review
# Preparing worktree (new branch 'acme-review')

# 커밋 하나만 잠깐 열어 볼 때
git worktree add --detach ../acme-check ed518e3
# Preparing worktree (detached HEAD ed518e3)

세 가지 형태만 알면 충분합니다. 새 브랜치, 폴더 이름 브랜치, 그리고 브랜치 없는 확인용입니다.

제대로 붙었는지 확인하는 법

새로 만든 폴더에는 .git 이 폴더가 아니라 파일로 들어 있습니다. 열어 보면 gitdir: 로 시작하는 한 줄뿐이고, 본체 저장소 안의 관리 폴더를 가리킵니다. 공식 문서도 연결된 작업 폴더에서는 GIT_DIR 이 이 전용 디렉터리를, GIT_COMMON_DIR 이 본체의 .git 을 가리키도록 설정되며 그 설정이 최상위의 .git 파일에 들어간다고 적어 두었습니다. 이 한 줄이 두 폴더를 잇는 연결선입니다.

연결이 살아 있는지는 rev-parse 로 확인하는 편이 확실합니다. --git-dir 은 그 폴더 전용 관리 디렉터리를, --git-common-dir 은 본체의 .git 을 돌려줍니다. 두 값이 서로 다르면서 common 쪽이 본체를 가리키고 있으면 정상입니다. 스크립트에서 지금 위치가 연결된 작업 폴더인지 판단해야 할 때도 이 두 값을 비교하면 됩니다.

저장소 자체는 하나라는 점도 확인해 둘 만합니다. 새 폴더에서 커밋하면 본체에서 즉시 보이고, 한쪽에서 fetch 한 결과도 양쪽이 함께 씁니다. 다만 공유되는 범위가 커밋에서 끝나지 않습니다. Git 2.50.1 에서 확인해 보면 stash 도 폴더별로 나뉘지 않아, 본체에서 밀어 둔 stash 가 다른 작업 폴더의 stash list 에 그대로 보입니다. 공식 문서가 이 동작을 명시한 문장은 찾지 못했으므로 버전에 따라 다를 수 있다고 보되, 작업 폴더를 나눴다고 stash 까지 나뉜다고 기대하지는 않는 편이 안전합니다.

목록을 스크립트에서 읽어야 한다면 git worktree list --porcelain 을 씁니다. 경로와 HEAD, 브랜치가 한 줄에 하나씩 나오는 형식이라 파싱이 안정적입니다.

cat ~/work/acme-hotfix/.git
# gitdir: /Users/me/work/acme/.git/worktrees/acme-hotfix

git -C ~/work/acme-hotfix rev-parse --git-dir
# /Users/me/work/acme/.git/worktrees/acme-hotfix
git -C ~/work/acme-hotfix rev-parse --git-common-dir
# /Users/me/work/acme/.git

# 새 폴더에서 커밋하면 본체에서 바로 보인다
git -C ~/work/acme-hotfix commit -am "fix login"
git -C ~/work/acme log --oneline --all
# b3489c4 fix login
# ed518e3 feat
# beab590 init

# stash 는 폴더별로 나뉘지 않는다
git -C ~/work/acme stash push -m "wip-main"
git -C ~/work/acme-hotfix stash list
# stash@{0}: On main: wip-main

커밋도 stash 도 저장소 하나에 모입니다. 나뉘는 것은 작업 폴더와 체크아웃된 브랜치뿐입니다.

나뉘는 것은 작업 폴더이고, 저장소는 끝까지 하나입니다.

무시된 파일과 미추적 파일은 따라오지 않는다

새 폴더에 담기는 것은 그 브랜치가 추적하는 파일뿐입니다. .gitignore 에 걸린 항목이나 아직 커밋하지 않은 미추적 파일은 복사되지 않습니다. 본체에 .env 와 node_modules 가 있는 상태에서 새 작업 폴더를 만들어 목록을 비교하면 차이가 그대로 드러납니다.

실무에서 이 성질은 양쪽으로 작용합니다. 불편한 쪽은 새 폴더에서 곧바로 실행이 안 된다는 점입니다. 의존성 설치를 다시 해야 하고, .env 처럼 저장소에 넣지 않는 설정 파일은 손으로 복사해야 합니다. 급한 수정을 하러 왔는데 설치부터 기다리는 일이 생깁니다.

반대로 좋은 쪽은 산출물이 섞이지 않는다는 점입니다. 브랜치를 오가며 한 폴더에서 빌드하면 이전 브랜치의 산출물이 남아 원인 모를 동작을 만들지만, 폴더가 다르면 그런 오염이 없습니다. 오래 쓸 작업 폴더라면 설정 파일 복사와 의존성 설치를 짧은 스크립트로 만들어 두면 매번 같은 자리에서 멈추지 않습니다.

ls -a ~/work/acme
# .  ..  .env  .git  .gitignore  README.md  node_modules  src

ls -a ~/work/acme-hotfix
# .  ..  .git  .gitignore  README.md  src
#   ↑ .env 와 node_modules 는 오지 않았다

# 새 폴더에서 바로 쓰려면 두 가지를 챙긴다
cp ~/work/acme/.env ~/work/acme-hotfix/.env
(cd ~/work/acme-hotfix && npm ci)

추적되는 파일만 옵니다. 실행에 필요한 나머지는 새 폴더에서 다시 갖춰야 합니다.

일이 끝나면 지우는 순서

정리는 git worktree remove 로 합니다. 폴더를 지우고 관리 정보까지 함께 정리하는 명령이라, Finder 나 rm 으로 폴더만 없애는 것과 결과가 다릅니다. 다만 남은 변경이 있으면 지우지 않습니다. 수정된 파일이 있을 때는 물론이고, 커밋하지 않은 미추적 파일이 하나만 있어도 같은 문구로 거부합니다. 잃을 것이 없는지 확인한 뒤 --force 를 붙이라는 뜻입니다.

작업 폴더를 지워도 그 자리에서 만든 브랜치는 남습니다. 병합이 끝났다면 git branch -d 로 따로 정리해야 목록이 깨끗해집니다. 반대로 브랜치를 실수로 지울 걱정은 덜어도 됩니다. 다른 작업 폴더가 체크아웃하고 있는 브랜치는 -D 를 줘도 지워지지 않고, 어느 폴더가 쓰고 있는지 경로까지 알려 줍니다.

외장 디스크나 네트워크 공유에 작업 폴더를 두었다면 잠가 두는 편이 안전합니다. 공식 문서는 항상 연결돼 있지 않은 장치 위의 작업 폴더는 관리 파일이 자동으로 정리되는 것을 막기 위해 잠그라고 안내합니다. 잠긴 폴더는 remove 도 거부하고, 이유까지 함께 보여 줍니다.

git worktree remove ../acme-hotfix
# fatal: '../acme-hotfix' contains modified or untracked files, use --force to delete it
# exit=128

# 남은 것이 없는지 확인한 뒤에 지운다
git -C ../acme-hotfix status --short
git worktree remove ../acme-hotfix        # 필요하면 --force

# 브랜치는 따로 남는다
git branch --list
# + acme-review
#   feature/api
#   hotfix/login
# * main
git branch -d hotfix/login

# 다른 폴더가 쓰는 브랜치는 지워지지 않는다
git branch -D acme-review
# error: cannot delete branch 'acme-review' used by worktree at '/Users/me/work/acme-review'

# 외장 디스크에 둔 폴더는 잠가 둔다
git worktree lock --reason "외장 디스크에 있음" ../acme-review
git worktree remove ../acme-review
# fatal: cannot remove a locked working tree, lock reason: 외장 디스크에 있음
# use 'remove -f -f' to override or unlock first

git branch --list 앞의 + 표시가 다른 작업 폴더에서 열려 있는 브랜치입니다.

브랜치 하나는 한 폴더에서만 열린다

가장 자주 걸리는 지점입니다. 이미 다른 작업 폴더가 체크아웃한 브랜치는 새 폴더에서 열 수 없습니다. add 로 열려고 해도, 이미 만들어 둔 폴더에서 switch 로 옮겨 가려 해도 같은 문구가 나옵니다. 어느 경로가 그 브랜치를 쓰고 있는지 함께 알려 주므로, 그 폴더로 가면 됩니다.

공식 문서는 add 가 지정한 대상이 브랜치 이름이고 그것이 다른 작업 폴더에 이미 체크아웃돼 있으면 기본적으로 거부하며, --force 가 이 안전장치를 덮어쓴다고 적어 두었습니다. 덮어쓸 수는 있지만 권하지 않습니다. 같은 브랜치를 두 폴더에서 열어 두면 한쪽에서 만든 커밋이 다른 쪽의 기준을 흔들고, 서로 다른 상태로 같은 브랜치를 밀게 됩니다.

이 제약은 불편해 보이지만 실제로는 사고를 막아 주는 쪽입니다. 브랜치와 폴더가 일대일로 묶이므로, 지금 보고 있는 창이 어느 브랜치인지 헷갈릴 일이 없습니다. main 을 두 곳에서 봐야 한다면 브랜치를 여는 대신 --detach 로 커밋만 열어 두는 방법이 있습니다. 그 폴더는 어떤 브랜치도 점유하지 않습니다.

git worktree add ../acme-main main
# Preparing worktree (checking out 'main')
# fatal: 'main' is already used by worktree at '/Users/me/work/acme'
# exit=128

# 이미 만든 폴더에서 옮겨 가려 해도 같다
git -C ../acme-hotfix switch main
# fatal: 'main' is already used by worktree at '/Users/me/work/acme'
# exit=128

# 같은 커밋을 봐야 할 뿐이라면 브랜치를 열지 않는다
git worktree add --detach ../acme-main main
# Preparing worktree (detached HEAD beab590)

브랜치를 점유하지 않는 --detach 는 이 제약을 피하는 정식 통로입니다.

한 브랜치는 한 폴더에서만 열립니다. 이 제약이 두 창의 상태가 엇갈리는 사고를 막습니다.

폴더를 손으로 옮기거나 지우면 연결이 끊긴다

연결선은 양방향입니다. 작업 폴더의 .git 파일이 본체를 가리키고, 본체의 관리 디렉터리 안에 있는 gitdir 파일이 작업 폴더를 되가리킵니다. Finder 로 폴더를 옮기면 뒤쪽이 어긋납니다. 그런데 옮긴 폴더에서 git status 는 그대로 동작하기 때문에 문제를 알아채지 못합니다. 어긋난 사실은 본체에서 git worktree list 를 볼 때 줄 끝에 붙는 prunable 표시로만 드러납니다.

여기서 아무도 정리하지 않으면 시한폭탄이 됩니다. 공식 문서는 git worktree remove 를 쓰지 않고 지운 작업 폴더의 관리 파일은 결국 자동으로 정리되거나 prune 으로 정리할 수 있다고 설명합니다. 자동 정리 시점은 gc.worktreePruneExpire 가 정하는데, Git 2.50.1 문서 기준 기본값은 3개월 전입니다. 즉 옮겨 둔 폴더는 한동안 멀쩡히 쓰이다가 어느 날 gc 가 돌면서 조용히 끊깁니다.

정리 명령을 직접 돌릴 때도 마찬가지입니다. prune 을 실행하면 그 폴더의 관리 정보가 지워지고, 그때부터 폴더 안에서는 저장소가 아니라는 오류만 돌아옵니다. 그래서 prune 은 -n 을 먼저 붙여 무엇이 지워질지 보고 실행하는 습관이 좋습니다. 이미 옮긴 폴더가 있다면 지우기 전에 repair 로 연결을 되살립니다. 공식 문서도 gitdir 파일을 손으로 고치는 대신 git worktree repair 를 실행해 연결을 자동으로 회복하라고 권합니다.

끊긴 뒤에는 되살릴 방법이 마땅치 않습니다. prune 이 지나간 폴더에 repair 를 걸면 .git 파일이 저장소를 가리키지 않는다며 실패하고, 그 자리에 다시 add 하려 해도 폴더가 이미 있다는 이유로 거부합니다. 남는 방법은 새 경로에 작업 폴더를 다시 만들고 편집하던 파일만 옮겨 담는 것입니다. 커밋은 본체에 남아 있으니 잃는 것은 커밋하지 않은 변경뿐이지만, 그 변경이야말로 이 절차를 시작한 이유였습니다. 폴더를 옮길 일이 생기면 처음부터 git worktree move 를 쓰는 편이 낫습니다.

# 손으로 옮기면 본체 목록에만 표시가 남는다
mv ~/work/acme-review ~/work/archive/acme-review
git worktree list
# /Users/me/work/acme         beab590 [main]
# /Users/me/work/acme-review  beab590 [acme-review] prunable

# 지우기 전에 무엇이 지워질지 먼저 본다
git worktree prune -n -v
# Removing worktrees/acme-review: gitdir file points to non-existent location

# 옮긴 것이라면 지우지 말고 연결을 되살린다
git worktree repair ~/work/archive/acme-review
# repair: gitdir incorrect: /Users/me/work/acme/.git/worktrees/acme-review/gitdir

# 이미 prune 이 지나갔다면 되살아나지 않는다
git worktree repair ~/work/archive/acme-review
# error: unable to locate repository; .git file does not reference a repository: ...
git worktree add ~/work/archive/acme-review acme-review
# fatal: '/Users/me/work/archive/acme-review' already exists

# 처음부터 이렇게 옮긴다
git worktree move ~/work/acme-review ~/work/archive/acme-review

prune 전에는 repair 로 돌아오지만, prune 뒤에는 돌아오지 않습니다.

조치 후 확인할 것

  • git worktree list 를 본체에서 돌려 열려 있는 폴더와 브랜치를 확인합니다. 줄 끝에 prunable 이 붙어 있으면 그 자리부터 repair 로 되살릴지 prune 으로 지울지 정합니다.
  • 새 폴더에서 실행까지 하려면 .env 같은 미추적 설정 파일을 복사하고 의존성을 다시 설치했는지 확인합니다. 추적되는 파일만 따라옵니다.
  • 작업 폴더를 옮길 때는 Finder 나 mv 대신 git worktree move 를 씁니다. 이미 손으로 옮겼다면 prune 이 돌기 전에 git worktree repair 를 실행합니다.
  • 일이 끝나면 git worktree remove 로 폴더를 정리하고, 그 자리에서 만든 브랜치는 git branch -d 로 따로 지웠는지 확인합니다.
  • 저장소 폴더 안쪽에 작업 폴더를 만들었다면 git status 에 미추적 항목으로 잡히지 않는지 확인합니다.

확인한 문서

정리하면 순서는 짧습니다. 목록을 한 번 보고, 형제 폴더에 add 로 새 작업 폴더를 열고, .git 파일과 rev-parse 로 연결을 확인합니다. 필요한 설정 파일과 의존성을 그 폴더에 갖춘 뒤 일을 하고, 끝나면 remove 와 branch -d 로 정리합니다. 기억할 것은 두 가지뿐입니다. 브랜치 하나는 한 폴더에서만 열린다는 것, 그리고 폴더를 옮기거나 지울 때는 반드시 move 와 remove 를 거친다는 것입니다.