재우니 개발자 블로그

Git Worktree: 개발자가 몰라서 손해보는 브랜치 동시 작업의 기술

"A 기능 개발 중인데, 갑자기 긴급 버그 수정 요청이 왔다."
이 상황에서 당신은 어떻게 하시나요?


들어가며: 당신은 어떤 방법을 쓰고 있나요?

실무를 하다 보면 반드시 이런 순간이 찾아옵니다. feature/payment 브랜치에서 결제 모듈을 한창 작업하고 있는데, 팀장님이 다가와 "운영 서버에 장애 났어요, 지금 당장 핫픽스 올려야 합니다"라고 합니다.

이 순간 대부분의 개발자는 다음 세 가지 방법 중 하나를 선택합니다.

 

 

방법 문제점
git stash 후 브랜치 전환 stash 충돌, 작업 컨텍스트 손실 위험
임시 커밋 후 브랜치 전환 커밋 히스토리 오염 (WIP, 임시 저장 커밋)
프로젝트를 새 폴더에 git clone 저장소 용량 2배, 클론 시간 낭비

 

 

세 방법 모두 불편하거나, 나중에 후폭풍이 남거나, 시간이 낭비됩니다. Git Worktree는 이 문제를 근본적으로 해결합니다.


 

 

Git Worktree란?

 

Git Worktree는 하나의 .git 저장소를 공유하면서, 여러 브랜치를 각각 다른 폴더에 동시에 체크아웃해서 작업할 수 있게 해주는 Git 내장 기능입니다.

 

  • Git 2.5 버전부터 내장 — 별도 설치 없이 바로 사용 가능
  • 기존 작업 폴더와 내용은 절대 건드리지 않음
  • .git 폴더(커밋 히스토리, 설정 등)는 하나만 존재 — 저장소 용량 이중 소모 없음

 

my-api-project/          ← 원래 작업 폴더 (feature/payment 브랜치)
  ├── .git/              ← 이 하나의 .git을 두 폴더가 공유
  ├── src/
  └── ...

my-api-hotfix/           ← worktree가 새로 만든 폴더 (hotfix/bug-01 브랜치)
  ├── src/               ← .git 폴더 없음. .git 파일(참조)만 존재
  └── ...

 

 


 

 

🚨 시작 전 반드시 알아야 할 핵심 제약

동일한 브랜치를 두 개의 worktree에서 동시에 체크아웃할 수 없습니다.

 

 

이것이 Git Worktree의 유일하지만 가장 중요한 제약입니다.

# 현재 메인 폴더가 main 브랜치를 체크아웃한 상태라면
git worktree add ../my-api-copy main

# ❌ 아래 오류 발생
# fatal: 'main' is already checked out at 'C:/Dev/my-api-project'

 

 

각 worktree 폴더는 반드시 서로 다른 브랜치를 바라보고 있어야 합니다.
이 제약은 Git의 설계 원칙(하나의 브랜치는 하나의 HEAD만 가짐)에서 비롯된 것으로, 당연한 이유가 있습니다.


 

 

Step by Step 실전 가이드

시나리오

  • 현재 위치: C:\Dev\my-api-project
  • 현재 브랜치: feature/payment (한창 개발 중)
  • 긴급 요청: main 기반으로 hotfix/bug-01 작성 필요

Step 1. 새 worktree 추가하기

상황에 맞는 명령어를 선택합니다.

상황 A: main을 베이스로 새 핫픽스 브랜치를 만들면서 작업 시작 (가장 흔한 케이스)

git worktree add -b hotfix/bug-01 ../my-api-hotfix main

 

  • -b hotfix/bug-01 : 새 브랜치를 생성
  • ../my-api-hotfix : 생성할 폴더 경로 (현재 폴더 바깥에 생성)
  • main : 어느 브랜치를 기준점으로 삼을지 (베이스 커밋)

 

 

상황 B: 이미 원격 저장소에 존재하는 브랜치를 로컬에 열어야 할 때 (코드 리뷰, 협업)

# fetch를 먼저 실행해서 원격 정보를 최신화하는 것을 권장
git fetch origin

git worktree add ../my-api-hotfix origin/hotfix/bug-01

 

 

주의 (상황 B): origin/ 접두사 없이 브랜치명만 입력하면, 해당 이름의 로컬 브랜치가 없을 경우 오류가 발생합니다. origin/을 붙이면 원격 브랜치를 직접 참조합니다. 이 방식은 detached HEAD 상태로 시작되므로, 커밋이 필요하다면 git checkout -b hotfix/bug-01로 로컬 브랜치를 만들어야 합니다.

 


 

 

Step 2. 두 폴더를 동시에 IDE에서 열기

새로 생긴 my-api-hotfix 폴더를 VS Code나 Visual Studio에서 새 창(New Window) 으로 엽니다.

  • 기존 창 → feature/payment 결제 기능 개발 계속
  • 새 창 → hotfix/bug-01 긴급 버그 수정

 

⚠️ ASP.NET Core 개발자 필독: 포트 충돌 방지

두 프로젝트를 동시에 로컬에서 실행하면 같은 포트를 점유하려고 해서 오류가 발생합니다.

새 워크트리 폴더의 Properties/launchSettings.json에서 포트를 변경해 주세요.

// my-api-hotfix/Properties/launchSettings.json
{
  "profiles": {
    "MyApiProject": {
      "commandName": "Project",
      "applicationUrl": "https://localhost:5003;http://localhost:5002",
      // ↑ 기존 5001/5000 대신 5003/5002로 변경
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

 

 

⚠️ dotnet restore 필요 여부

새 폴더는 물리적으로 새로 생성된 디렉토리이므로, bin/, obj/ 폴더가 존재하지 않습니다. 빌드 전에 한 번 실행해 주세요.

cd ../my-api-hotfix
dotnet restore

 


Step 3. 현재 worktree 상태 확인하기

git worktree list

 

C:/Dev/my-api-project      1a2b3c4 [feature/payment]
C:/Dev/my-api-hotfix       5d6e7f8 [hotfix/bug-01]

 

커밋 해시와 브랜치명이 함께 표시되므로, 여러 worktree를 운영할 때 현재 상태를 한눈에 파악할 수 있습니다.


Step 4. 작업 완료 후 worktree 삭제하기

핫픽스 작업을 마치고 원격 저장소에 Push까지 완료했다면, 임시 폴더를 정리합니다. 원래 작업하던 폴더의 터미널에서 실행합니다.

git worktree remove ../my-api-hotfix

 

 

🚨 트러블슈팅: 삭제가 거부되는 경우

빌드 산출물(bin/, obj/)이나 커밋되지 않은 파일(Untracked files)이 남아있으면 Git이 안전을 위해 삭제를 막습니다.

error: '../my-api-hotfix' contains modified or untracked files,
       use --force to delete it.

--force 옵션으로 강제 삭제합니다.

git worktree remove --force ../my-api-hotfix

 

 

참고: --force는 파일 시스템의 폴더 자체를 삭제하는 것으로, 로컬 브랜치(hotfix/bug-01)는 영향을 받지 않습니다. 브랜치 히스토리는 그대로 보존됩니다.


Step 5. (번외) 폴더를 실수로 탐색기에서 직접 삭제했을 때

git worktree remove 대신 윈도우 탐색기나 rm -rf 명령으로 폴더를 직접 지운 경우, .git 내부에 없어진 폴더를 참조하는 dead reference가 남습니다.

git worktree prune

 

 

이 명령어가 없어진 worktree 폴더의 참조를 자동으로 감지하고 정리합니다. 정기적으로 실행해도 안전합니다.


명령어 전체 요약

# 새 브랜치를 만들면서 worktree 추가 (가장 흔한 케이스)
git worktree add -b <새브랜치명> <폴더경로> <베이스브랜치>

# 기존 브랜치(원격)를 worktree로 추가
git worktree add <폴더경로> origin/<브랜치명>

# 현재 worktree 목록 확인
git worktree list

# worktree 삭제 (정상 삭제)
git worktree remove <폴더경로>

# worktree 강제 삭제 (untracked 파일 있을 때)
git worktree remove --force <폴더경로>

# 수동으로 지운 폴더의 dead reference 정리
git worktree prune

 


실무에서 Worktree가 빛나는 3가지 상황

1. 긴급 핫픽스 대응

본문에서 다룬 시나리오입니다. 진행 중인 기능 개발을 stash 없이 그대로 유지하면서, 별도 폴더에서 핫픽스를 작업합니다.

 

2. 코드 리뷰 (PR Review)

팀원의 PR을 리뷰할 때, 단순히 코드를 눈으로 보는 것을 넘어서 실제로 실행해서 동작을 확인해야 할 경우가 있습니다.

git fetch origin
git worktree add ../review-pr-123 origin/feature/new-dashboard

 

자신의 작업 브랜치는 그대로 두고, 별도 폴더에서 PR 브랜치를 실행해볼 수 있습니다.

 

 

3. 릴리즈 브랜치와 개발 브랜치 동시 관리

배포 후 릴리즈 브랜치(release/v2.1)에서 소규모 패치를 진행하면서, 동시에 develop 브랜치에서 다음 버전 개발을 이어가야 하는 경우에 이상적입니다.


 

 

마치며

Git Worktree는 화려한 기능이 아닙니다. 하지만 정확히 필요한 순간에 정확히 문제를 해결해 주는, 실무형 기능입니다.

git stash로 컨텍스트를 날리고, WIP 커밋으로 히스토리를 오염시키고, 이중 클론으로 저장소를 낭비하던 습관을 이 기능 하나로 깔끔하게 끊어낼 수 있습니다.

 

특히 ASP.NET Core나 Spring Boot 같은 백엔드 프레임워크 환경에서는 포트 충돌과 dotnet restore / mvn install 등의 초기 세팅만 신경 써주면, 이후에는 별다른 불편함 없이 사용할 수 있습니다.

Git 2.5 이상이라면 지금 당장 터미널을 열고 연습해 보시길 권합니다. 한번 손에 익히면 다시는 이전으로 돌아가기 어려운 기능입니다.

 

 


 

 

추후 긴급 패치 MAIN 원격지 PUSH 하고 나서, 추후에 이를 원격지에서 PULL 할 경우 충돌 발생이 있습니다. 이를 위한 가이드이며 순차 명령에 맞게 사용하시면 되겠습니다.

 

순차 명령 (설명 + 실제 명령)

현재 상태 확인: 로컬 변경 및 브랜치 상태 확인
git status --porcelain --branch

원격 정보 가져오기: 원격 변경만 가져옴
git fetch origin

원격 병합 시도(초기): 바로 풀하면 로컬 변경으로 중단됨
git pull origin main

로컬 변경 임시저장: 추적/미추적 파일 포함해서 stash
git stash push -u -m "auto-stash: before pull origin/main"

저장된 stash 확인:
git stash list

원격에서 변경 가져와 병합: stash 후 다시 pull(성공, fast-forward)
git pull origin main

stash 다시 적용: 로컬 변경 복원 시도(여기서 충돌 발생)
git stash pop

충돌 수동 해결: 충돌 편집 후 저장 (Views/Home/Index.cshtml 수동 편집)

해결된 파일 스테이징:
git add Views/Home/Index.cshtml

병합 충돌 해결 커밋:
git commit -m "Resolve merge conflict: Index.cshtml"

남은 stash 확인 및 제거(선택):
git stash list
git stash drop stash@{0}

병합 결과 원격 푸시:
git push origin main

최종 상태 확인:
git status --short --branch