Git worktree๋ ํ๋์ Git ์ ์ฅ์์์ ์ฌ๋ฌ ๊ฐ์ ์์ ํธ๋ฆฌ(working tree)๋ฅผ ๋์์ ๊ด๋ฆฌํ ์ ์๊ฒ ํด์ฃผ๋ ๊ธฐ๋ฅ์ ๋๋ค. ์ด๋ฅผ ํตํด ๋ธ๋์น ์ ํ ์์ด ์ฌ๋ฌ ๋ธ๋์น์์ ๋์์ ์์ ํ ์ ์์ต๋๋ค.
ํ๋์ ์ ์ฅ์๋ ๋ค์์ ๊ฐ์ง ์ ์์ต๋๋ค:
- Main worktree:
git init๋๋git clone์ผ๋ก ์์ฑ๋ ๊ธฐ๋ณธ ์์ ํธ๋ฆฌ (์ ์ฅ์๋น 1๊ฐ) - Linked worktrees:
git worktree add๋ก ์์ฑ๋ ์ถ๊ฐ ์์ ํธ๋ฆฌ (์ฌ๋ฌ ๊ฐ ๊ฐ๋ฅ)
ํด๋น ๊ฐ๋ ์ด ํ์ํ ์ด์
- ๊ธด๊ธ ์์ ์ ์์ ๋ณด์กด: ๋ฆฌํฉํ ๋ง ์ค ๊ธด๊ธ ๋ฒ๊ทธ ์์ ์ด ํ์ํ ๋, stash ์์ด ๋ณ๋ worktree์์ ์์
- ๋ณ๋ ฌ ๊ฐ๋ฐ: ์ฌ๋ฌ ๊ธฐ๋ฅ์ ๋์์ ๊ฐ๋ฐํ๋ฉฐ ๊ฐ๊ฐ ๋ ๋ฆฝ๋ ์์ ๊ณต๊ฐ ์ ์ง
- ๋ธ๋์น ๋น๊ต ํ ์คํธ: ์ฌ๋ฌ ๋ธ๋์น๋ฅผ ๋์์ ์ฒดํฌ์์ํ์ฌ side-by-side ๋น๊ต ๋ฐ ํ ์คํธ
- ์คํ์ ๋ณ๊ฒฝ: ๋ฉ์ธ ์์ ์ ์ํฅ ์์ด โ์ผํ์ฉโ worktree์์ ์คํ
AS-IS
๋ธ๋์น ์ ํ ๋ฐฉ์์์๋ ๊ธด๊ธ ์์ ์ ์ํด ํ์ฌ ์์ ์ stashํ๊ฑฐ๋ ์ปค๋ฐํด์ผ ํฉ๋๋ค.
sequenceDiagram autonumber participant Dev participant Git participant Boss Dev->>Git: refactoring ๋ธ๋์น์์ ์์ ์ค Note over Dev: ์ฝ๋ ๋ฆฌํฉํ ๋ง ์งํ ์ค (๋ฏธ์์ฑ) Boss->>Dev: ๊ธด๊ธ ๋ฒ๊ทธ ์์ ํ์! (main ๋ธ๋์น) Dev->>Git: git stash (์์ ์์ ์ ์ฅ) Dev->>Git: git checkout main Dev->>Git: ๋ฒ๊ทธ ์์ ํ ์ปค๋ฐ Dev->>Git: git checkout refactoring Dev->>Git: git stash pop (์์ ๋ณต์) Note over Dev: ์ถฉ๋ ํด๊ฒฐ ํ์ํ ์๋...
TO-BE
Worktree๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ฌ ์์ ์ ์ ์งํ ์ฑ ๋ณ๋ ๊ณต๊ฐ์์ ์์ ํ ์ ์์ต๋๋ค.
sequenceDiagram autonumber participant Dev participant Main at /project participant Temp at /temp (hotfix ๋ธ๋์น) participant GitHub participant Boss Dev->>Main: refactoring ๋ธ๋์น์์ ์์ ์ค Note over Dev: ์ฝ๋ ๋ฆฌํฉํ ๋ง ์งํ ์ค (๋ฏธ์์ฑ) Boss->>Dev: ๊ธด๊ธ ๋ฒ๊ทธ ์์ ํ์! Dev->>Temp: git worktree add -b hotfix ../temp main Note over Temp: main ๊ธฐ๋ฐ์ผ๋ก hotfix ๋ธ๋์น ์์ฑ Dev->>Temp: cd ../temp Dev->>Temp: ๋ฒ๊ทธ ์์ ์์ Dev->>Temp: git commit -m "fix: critical bug" Note over Temp: hotfix ๋ธ๋์น์ ์ปค๋ฐ (main ์๋!) Dev->>GitHub: git push origin hotfix Dev->>GitHub: PR ์์ฑ (hotfix -> main) Note over GitHub: ์ฝ๋ ๋ฆฌ๋ทฐ & ๋จธ์ง Dev->>Temp: git worktree remove ../temp Dev->>Main: cd /project (refactoring ์์ ๊ณ์) Note over Main: ์์ ๋ด์ฉ ๊ทธ๋๋ก ์ ์ง๋จ
Worktrees vs Branches ๋น๊ต
| ์ธก๋ฉด | Branches | Worktrees |
|---|---|---|
| ์ฒดํฌ์์ ์ํ | Worktree๋น ํ๋ | ์ฌ๋ฌ ๋ธ๋์น ๋์ ์ฒดํฌ์์ |
| ์์ ๋๋ ํ ๋ฆฌ | ๋จ์ผ ๋๋ ํ ๋ฆฌ | ์ฌ๋ฌ ๋๋ ํ ๋ฆฌ |
| ์ฉ๋ | ์ปค๋ฐ ํ์คํ ๋ฆฌ ๊ตฌ์ฑ | ๋ณ๋ ฌ ๊ฐ๋ฐ ์ํฌํ๋ก์ฐ |
| ์ ํ ๋น์ฉ | ๋น ๋ฆ (ํฌ์ธํฐ๋ง ์ ๋ฐ์ดํธ) | ๋๋ ํ ๋ฆฌ ์ด๋ ํ์ |
| ๋ฆฌ์์ค ์ฌ์ฉ | ์ต์ | ๋ ๋ง์ ๋์คํฌ ๊ณต๊ฐ |
| ๋ณต์ก๋ | ๋จ์ | ๋ ๊ณ ๊ธ |
| Stash ํ์ | ์์ฃผ ํ์ | ๋ถํ์ |
Worktrees๋ฅผ ์ฌ์ฉํด์ผ ํ ๋
โ ๋ฆฌํฉํ ๋ง ์ค ๊ธด๊ธ ์์
git worktree add -b emergency-fix ../temp master
cd ../temp
# ๋ฒ๊ทธ ์์
git commit -a -m 'emergency fix'
cd -
git worktree remove ../tempโ ์ฌ๋ฌ ๋ธ๋์น ๋์ ํ ์คํธ
git worktree add ../test-v1.0 v1.0
git worktree add ../test-v2.0 v2.0
# ๋ธ๋์น ์ ํ ์์ด ๋ณ๋ ฌ ํ
์คํธโ ๋ณต์กํ ์คํ
git worktree add -d ../experiment
# ๋ฉ์ธ ์์
์ ์ํฅ ์์ด ์คํBranches๋ฅผ ์ฌ์ฉํด์ผ ํ ๋
โ ์ฝ๋ ํ์คํ ๋ฆฌ ๊ตฌ์ฑ
- ๋ธ๋์น๋ ๊ฐ๋ฐ ํ์๋ผ์ธ์ ๋ํ๋
โ ๊ฐ๋จํ ๊ธฐ๋ฅ ์ ํ
git checkout์ผ๋ก ๋น ๋ฅธ ๋ธ๋์น ์ ํ- ์ ์ ๋ฆฌ์์ค ์ค๋ฒํค๋
โ ์ผ๋ฐ์ ์ธ Git ์ํฌํ๋ก์ฐ
- ๋๋ถ๋ถ์ ํ์ค ์ํฌํ๋ก์ฐ๋ ๋จ์ผ ์์ ๋๋ ํ ๋ฆฌ๋ฅผ ๊ฐ์
์ฃผ์ ์ํฌํ๋ก์ฐ ์๋๋ฆฌ์ค
์๋๋ฆฌ์ค 1: ๋ฆฌํฉํ ๋ง ์ค ๊ธด๊ธ ์์
sequenceDiagram autonumber participant Dev participant Main as /project (refactoring) participant Temp as /temp (emergency-fix) participant GitHub Note over Main: refactoring ๋ธ๋์น ์์ ์ค Dev->>Temp: git worktree add -b emergency-fix ../temp master Note over Temp: master ๊ธฐ๋ฐ ์ ๋ธ๋์น ์์ฑ Dev->>Temp: cd ../temp Dev->>Temp: ๋ฒ๊ทธ ์์ & ์ปค๋ฐ Dev->>GitHub: git push origin emergency-fix Dev->>GitHub: PR ์์ฑ ๋ฐ ๋จธ์ง Dev->>Main: cd /project Dev->>Temp: git worktree remove ../temp Note over Main: refactoring ์์ ๊ณ์ (์ํฅ ์์)
์๋๋ฆฌ์ค 2: ๋ณ๋ ฌ ๊ธฐ๋ฅ ๊ฐ๋ฐ
flowchart TD A["Main Worktree /project (master)"] --> B[git worktree add ../feature-a] A --> C[git worktree add ../feature-b] A --> D[git worktree add -d ../testing] B --> E["Worktree A /feature-a (feature-branch-a)"] C --> F["Worktree B /feature-b (feature-branch-b)"] D --> G["Worktree C /testing (detached HEAD)"] E --> H[ํ์ A ์์ ] F --> I[ํ์ B ์์ ] G --> J[ํตํฉ ํ ์คํธ] H --> K[git push & PR] I --> L[git push & PR] J --> M[ํ ์คํธ ๊ฒฐ๊ณผ ํ์ธ] K --> N[main์ผ๋ก ๋จธ์ง] L --> N
์๋๋ฆฌ์ค 3: ํด๋์ฉ ์ฅ์น ์ค์
sequenceDiagram autonumber participant Dev participant Main at /project participant USB at /media/usb/project participant Git Dev->>USB: git worktree add --lock /media/usb/project Note over USB: --lock์ผ๋ก ์ค์ ์ญ์ ๋ฐฉ์ง Dev->>Git: git worktree list -v Note over Git: /project [master]<br/>/media/usb/project (feature) locked Note over Dev: USB ์ฅ์น ์ธ๋ง์ดํธ Note over USB: ๋ฌผ๋ฆฌ์ ์ผ๋ก ์ฐ๊ฒฐ ํด์ Note over Dev: USB ์ฅ์น ์ฌ๋ง์ดํธ Dev->>USB: cd /media/usb/project Dev->>USB: git status Note over USB: ์ ์ ์๋! Dev->>USB: ์์ ์๋ฃ Dev->>Git: git worktree unlock /media/usb/project Dev->>Git: git worktree remove /media/usb/project
์๋๋ฆฌ์ค 4: ๋จธ์ง ์ ํ ์คํธ
sequenceDiagram autonumber participant Dev participant Main as /project (main) participant Test as /testing (test-feature) participant CI Note over Main: feature-branch ๊ฐ๋ฐ ์๋ฃ Dev->>Test: git worktree add -b test-feature ../testing feature-branch Note over Test: feature-branch ๋ณต์ฌ๋ณธ ์์ฑ Dev->>Test: cd ../testing Dev->>Test: npm test Dev->>Test: python -m pytest Note over Test: ์ ์ฒด ํ ์คํธ ์ค์ํธ ์คํ alt ํ ์คํธ ํต๊ณผ Dev->>CI: ํ ์คํธ ์ฑ๊ณต ํ์ธ Dev->>Main: git worktree remove ../testing Dev->>Main: git checkout main Dev->>Main: git merge feature-branch Note over Main: ๋จธ์ง ์งํ else ํ ์คํธ ์คํจ Dev->>CI: ํ ์คํธ ์คํจ ํ์ธ Dev->>Main: git worktree remove -f ../testing Dev->>Main: feature-branch์์ ์ด์ ์์ Note over Main: ๋ค์ ํ ์คํธ ๋ฐ๋ณต end
Worktree ํ์
Main worktree
git init๋๋git clone์ผ๋ก ์์ฑ- ์ ์ฅ์๋น ํ๋๋ง ์กด์ฌ
- ์ญ์ ๋ถ๊ฐ๋ฅ
.git/๋๋ ํ ๋ฆฌ๊ฐ ์ค์ Git ์ ์ฅ์
Linked worktrees
git worktree add๋ก ์์ฑ- ์ฌ๋ฌ ๊ฐ ์์ฑ ๊ฐ๋ฅ
- ์ญ์ ๊ฐ๋ฅ
.gitํ์ผ์ด main worktree์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ๊ฐ๋ฆฌํด
๋๋ ํ ๋ฆฌ ๊ตฌ์กฐ:
/path/main/ # Main worktree
โโโ .git/ # ์ค์ Git ์ ์ฅ์
โ โโโ worktrees/ # Linked worktrees ๋ฉํ๋ฐ์ดํฐ
โ โ โโโ hotfix/ # Linked worktree 1
โ โ โโโ HEAD # Per-worktree
โ โ โโโ index # Per-worktree
โ โ โโโ gitdir # ์์น ํฌ์ธํฐ
โ โโโ refs/heads/ # ๊ณต์ (๋ชจ๋ worktree)
โ โโโ objects/ # ๊ณต์ (๋ชจ๋ worktree)
โโโ source-files/
/path/other/hotfix/ # Linked worktree
โโโ .git # ํ์ผ (๋๋ ํ ๋ฆฌ ์๋!)
โ ๋ด์ฉ: gitdir: /path/main/.git/worktrees/hotfix
โโโ source-files/
๊ณต์ vs Per-worktree ์ปดํฌ๋ํธ
| ์ปดํฌ๋ํธ | ๊ณต์ ์ฌ๋ถ |
|---|---|
HEAD | Per-worktree (๊ฐ worktree๋ง๋ค ๋ค๋ฆ) |
index (staging area) | Per-worktree |
refs/heads/* (๋ธ๋์น) | ๊ณต์ (๋ชจ๋ worktree) |
refs/tags/* (ํ๊ทธ) | ๊ณต์ |
objects/ (์ปค๋ฐ ๋ฐ์ดํฐ) | ๊ณต์ |
์ฃผ์ ๋ช ๋ น์ด
add - ์ worktree ์์ฑ
๊ธฐ๋ณธ ์ฌ์ฉ:
git worktree add <path> [<commit-ish>]์์:
# ๊ฒฝ๋ก ๊ธฐ๋ฐ ๋ธ๋์น๋ช
์๋ ์์ฑ
git worktree add ../hotfix
# "hotfix" ๋ธ๋์น๋ฅผ ์์ฑํ๊ณ ../hotfix์ ์ฒดํฌ์์
# ๊ธฐ์กด ๋ธ๋์น ์ฒดํฌ์์
git worktree add ../new-feature feature-branch
# ์ ๋ธ๋์น ์์ฑํ๋ฉฐ worktree ์ถ๊ฐ
git worktree add -b new-branch ../path main
# Detached HEAD (๋ธ๋์น ์์)
git worktree add -d ../experimental
# ์ ๊ธ๊ณผ ํจ๊ป ์์ฑ (์ค์๋ก ์ญ์ ๋ฐฉ์ง)
git worktree add --lock ../mounted-device์ฃผ์ ์ต์ :
-b <new-branch>: ์ ๋ธ๋์น ์์ฑ-B <new-branch>: ๋ธ๋์น ์์ฑ/๋ฆฌ์ -d, --detach: Detached HEAD ์ํ--lock: ์์ฑ ํ ์ ๊ธ--no-checkout: ์ฒดํฌ์์ ์๋ต
list - ๋ชจ๋ worktree ์กฐํ
git worktree list์ถ๋ ฅ ์์:
/path/to/main abcd1234 [master]
/path/to/linked-worktree 1234abc [feature-a]
/path/to/other 5678def (detached HEAD)
์ต์ :
git worktree list -v # ์์ธ ์ ๋ณด (์ ๊ธ ์ด์ ๋ฑ)
git worktree list --porcelain # ๊ธฐ๊ณ ํ๋
๊ฐ๋ฅ ํ์remove - worktree ์ญ์
git worktree remove <worktree>์์:
# ๊นจ๋ํ worktree ์ญ์
git worktree remove ../hotfix
# ์ปค๋ฐ๋์ง ์์ ๋ณ๊ฒฝ์ฌํญ์ด ์์ด๋ ๊ฐ์ ์ญ์
git worktree remove -f ../dirty-worktree
# ์ ๊ธด worktree ๊ฐ์ ์ญ์
git worktree remove -ff ../locked-worktreeprune - ์ค๋๋ ๋ฉํ๋ฐ์ดํฐ ์ ๋ฆฌ
# ์ค๋๋ ๊ด๋ฆฌ ํ์ผ ์ ๊ฑฐ
git worktree prune
# Dry run (์ญ์ ๋ ํญ๋ชฉ ํ์ธ)
git worktree prune -n
# ํน์ ์๊ฐ ์ด์ ํญ๋ชฉ๋ง ์ ๊ฑฐ
git worktree prune --expire=2.weeks.agolock / unlock - ์ค์ ์ญ์ ๋ฐฉ์ง
# Worktree ์ ๊ธ (์: ํด๋์ฉ ์ฅ์น)
git worktree lock ../portable-worktree
git worktree lock --reason "USB ๋๋ผ์ด๋ธ" ../usb-drive
# Worktree ์ ๊ธ ํด์
git worktree unlock ../portable-worktreemove - worktree ์์น ๋ณ๊ฒฝ
git worktree move <worktree> <new-path>repair - ๊นจ์ง worktree ๋งํฌ ๋ณต๊ตฌ
# Worktree ์ฐ๊ฒฐ ๋ณต๊ตฌ
git worktree repair
# ์๋์ผ๋ก ์ด๋ํ ํ ํน์ worktree ๋ณต๊ตฌ
git worktree repair /path/to/worktree1์ฌ์ฉ ์๋๋ฆฌ์ค:
- Main worktree๋ฅผ ์๋์ผ๋ก ์ด๋ํ ๊ฒฝ์ฐ
git worktree move์์ด linked worktree ์ด๋- ์์๋๊ฑฐ๋ ์ค๋๋ ๊ด๋ฆฌ ํ์ผ
์ค์
worktree.guessRemote
# ์๊ฒฉ ๋ธ๋์น ์๋ ๋งค์นญ ํ์ฑํ
$ git config worktree.guessRemote true
# ์ด์ ๋ค์์ด ์๋:
$ git worktree add ../feature-x
# ์กด์ฌํ๋ฉด remote/origin/feature-x ์๋ ๋งค์นญworktree.useRelativePaths
# ์ด์์ฑ์ ์ํด ์๋ ๊ฒฝ๋ก ์ฌ์ฉ
$ git config worktree.useRelativePaths true
# ์ ์ฅ์์ worktree๊ฐ ์์น ๊ฐ ์ด๋ํ ๋ ์ ์ฉWorktree๋ณ ์ค์
# Per-worktree config ํ์ฑํ
$ git config extensions.worktreeConfig true
# Worktree๋ณ ์ค์ : .git/config.worktree
$ git config --worktree core.sparseCheckout true์ฃผ์์ฌํญ
์ ํ์ฌํญ
โ ๏ธ Main worktree๋ ์ญ์ ๋ถ๊ฐ๋ฅ
โ ๏ธ Submodule์ด ์๋ worktree๋ git worktree move๋ก ์ด๋ ๋ถ๊ฐ
โ ๏ธ Multiple checkout ์ง์์ ์์ง ์คํ์ ๊ธฐ๋ฅ
โ ๏ธ Submodule ์ง์์ด ๋ถ์์
Best Practices
โ
์๋ ์ญ์ ๋์ ํญ์ git worktree remove ์ฌ์ฉ
โ
ํด๋์ฉ ์ฅ์น์ worktree๋ ์ ๊ธ ์ค์
โ
์๋ ์ด๋ ํ git worktree repair ์ฌ์ฉ
โ
Worktree๋ณ ์ค์ ์ด ํฌ๊ฒ ๋ค๋ฅด๋ฉด extensions.worktreeConfig ํ์ฑํ