# Git Worktree 多分支並行開發：一套不互相汙染的標準流程

> Canonical: https://vxi.me/zh-tw/posts/git-worktree-parallel-development/
> Locale: zh-tw
> Published: 2026-03-25
> Updated: 2026-03-25
> Markdown: https://vxi.me/zh-tw/posts/git-worktree-parallel-development.md

很多人第一次想做“多功能並行開發”時，直覺都是多開幾個分支，然後在一個儲存庫目錄裡來回 `checkout`。短時間看起來還能用，但只要你一邊改功能、一邊跑服務、一邊修線上問題，很快就會發現這套方式特別容易串。

我後來基本固定成了另一種做法：主目錄只保留穩定分支，真正開發時用 `git worktree` 給每個功能開一個獨立目錄。這樣你在同一台機器上可以同時維護多個功能分支，而且目錄、程序、建置產物基本都隔開了。

如果你現在在搜 Git 多分支並行開發、Git worktree 用法、Worktree 標準流程，這篇文章就是圍繞這些問題寫的。

## 為什麼我最後選了 Git Worktree，而不是反覆切分支

真正麻煩的從來不是“怎麼建立一個新分支”，而是你怎麼確保自己測試的就是這個分支、服務跑的也是這個目錄、最後提交時不會把別的功能殘留一起帶進去。

在一個儲存庫目錄裡反覆切分支，最容易出現幾類問題：

- 你以為自己在測新功能，結果服務還是從舊目錄啟動的
- 某個功能留下來的建置產物，汙染了另一個分支
- `git add .` 一把梭，把根本不該提交的檔案也帶上去了
- 一邊修線上 bug，一邊改新功能，目錄狀態越來越亂

`git worktree` 的好處就在這裡。它不是讓你“更快建立分支”而已，而是讓每個功能擁有自己單獨的工作目錄。對我來說，這個隔離感比什麼都重要。

## 我自己固定下來的目錄約定

為了避免後面越用越亂，我會先把幾個約定定死：

- 主儲存庫目錄：`/Users/kuangyongjie/charting_library`
- worktree 根目錄：`/Users/kuangyongjie/worktrees`
- 分支命名：`codex/<feature-name>`
- 主分支：`master`

也就是說，主目錄只負責保持穩定和合並。真正開發一個功能時，我會把它放到一個新的 worktree 目錄裡，比如：

```text
/Users/kuangyongjie/worktrees/hotspot-ff
```

這個約定看起來很小，但實際很有用。因為你只要看到路徑，就知道當前到底在主儲存庫，還是在某個功能分支目錄。

## 標準做法其實就幾步，但每一步都別省

先準備一下 worktree 根目錄：

```bash
mkdir -p /Users/kuangyongjie/worktrees
```

然後從主儲存庫建立一個獨立 worktree 和新分支：

```bash
REPO=/Users/kuangyongjie/charting_library
WT=/Users/kuangyongjie/worktrees/<feature-name>
BRANCH=codex/<feature-name>
BASE=master

# 如果远端可用，先更新 master
# git -C "$REPO" pull --rebase central "$BASE"

git -C "$REPO" worktree add "$WT" -b "$BRANCH" "$BASE"
cd "$WT"
git branch --show-current
```

正常情況下，最後應該看到：

```text
codex/<feature-name>
```

這一步很關鍵，因為它明確告訴你：從現在開始，這個功能的程式碼、建置、測試，都應該只發生在 `"$WT"` 裡。

## 只在分支目錄裡開發和測試，這條規則最好別破

後面無論你改前端、跑後端，還是做本地驗證，都只在這個 worktree 目錄裡做。

比如前端專案在子目錄裡，你就進這個分支目錄再建置：

```bash
cd "$WT/trendradar-service/trendradar-web"
npm run build
# 生产混淆构建
# npm run build:multi-layer
```

我自己踩過一個很常見的坑，就是程式碼明明改了，頁面卻還是舊效果。後來一查，不是程式碼沒生效，而是服務壓根跑在另一個目錄裡。

所以只要出現“我明明改了但沒變化”，我第一反應不是懷疑框架，而是先查當前程序的 `cwd`。

```bash
lsof -nP -iTCP:<port> | sed -n '1,5p'
ps -p <pid> -o pid,ppid,command
lsof -p <pid> | rg "cwd"
```

你最後必須確認 `cwd` 指向的是：

```text
/Users/kuangyongjie/worktrees/<feature-name>/...
```

不然你測試的東西很可能根本不是這個分支。

## 提交的時候，我基本不用 `git add .`

這一點我會故意保守一點。

只要是 worktree 並行開發，我提交前幾乎都會只加目標檔案，而不是直接 `git add .`。因為一旦你本地有執行產物、快取檔案、快照檔案，這一把很容易把髒東西也帶進去。

像這些東西就很常見：

- `__pycache__`
- `*.db`
- `snapshot_*`
- `.DS_Store`

我更傾向於這樣提：

```bash
cd "$WT"
git add <file1> <file2> <file3>
git status --short
git commit -m "feat/fix: <summary>"
```

先看一眼 `git status --short` 再 commit，這個動作雖然小，但能擋掉很多低階錯誤。

## 合併回主分支時，只在主目錄做

功能做完以後，合併動作我只會在主儲存庫目錄執行，不會在 worktree 裡順手 merge。

```bash
cd /Users/kuangyongjie/charting_library
git branch --show-current   # 必须是 master

# 如远端可用，先更新
# git pull --rebase central master

git merge --no-ff codex/<feature-name>
```

這裡的重點其實不是 `merge --no-ff` 本身，而是你要確認自己真的站在主目錄、主分支上。

如果你遇到：

```text
MERGE_HEAD exists
```

通常說明上次 merge 沒收乾淨，可以先：

```bash
git merge --abort
```

然後再重新執行 pull 和 merge。

如果你後面還要推遠端，再補一步：

```bash
cd /Users/kuangyongjie/charting_library
git push <remote> master
```

順手提醒一下，你這臺機器上常見遠端名是 `central`，不一定是 `origin``。這類問題通常是環境差異，不是 Git Worktree 本身的問題。

## 什麼情況下我會用 Fast-forward，而不是強行留 merge 記錄

如果你的要求是“分支單獨啟動測試，透過後再合併，而且不生成 merge 記錄”，那就可以走 fast-forward 的思路。

比如：

```bash
cd /Users/kuangyongjie/charting_library
git checkout master
git pull --rebase central master

git worktree add /Users/kuangyongjie/worktrees/hotspot-ff -b codex/hotspot-ff master
```

然後只在這個分支 worktree 裡啟動服務，比如：

```bash
cd /Users/kuangyongjie/worktrees/hotspot-ff/trendradar-service
./venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 9544
```

再開一個終端，確認當前程序的工作目錄確實就是這個分支：

```bash
lsof -nP -iTCP:9544 -sTCP:LISTEN
ps -p <PID> -o pid,ppid,command
lsof -a -p <PID> -d cwd -Fn
```

這個檢查動作非常值，因為很多“分支驗證透過”的結論，最後其實是建立在測錯目錄上的。

## 我現在基本固定下來的幾條規則

如果你也準備長期用 Worktree，我建議至少把下面幾條變成硬規則：

- 一個功能一個 worktree 目錄
- 一個對話或一個任務只對應一個 worktree
- 開工先跑 `git branch --show-current`
- 提交前先看 `git status --short`
- 合併只在主目錄 `master` 執行

這幾條聽起來很普通，但真正能讓並行開發不亂掉，靠的就是這些基礎動作。

## 常見問題

### 本地頁面還是舊效果

先別急著懷疑程式碼。

先查服務程序的 `cwd` 是不是在目標 worktree，再確認你是不是已經在那個分支目錄裡重新建置過前端產物。這個問題特別常見。

### `fatal: 'origin' does not appear to be a git repository`

一般不是 Git 掛了，而是遠端名寫錯了。先跑：

```bash
git remote -v
```

然後按實際遠端名 push。你這邊常見的是 `central`。

### `MERGE_HEAD exists`

說明上次 merge 沒結束。先：

```bash
git merge --abort
```

然後再重新走合併流程。

### 只改了前端，要不要重啟後端

通常不需要。很多時候靜態資源重新建置並部署成功後，重新整理快取就夠了。
