Git worktree:不打断当前分支,并行处理另一条分支

122 次浏览5 条回复

git worktree 可以让同一个仓库同时拥有多个工作目录。主目录继续保留当前开发分支,另一个目录专门处理修复或验证任务,省去反复暂存和切换。

环境:Git 2.39+;Linux 或 macOS;Bash。下面只操作新建的临时仓库。

root=$(mktemp -d)
repo="$root/demo"

git init -b main "$repo"
git -C "$repo" config user.name "Worktree Demo"
git -C "$repo" config user.email "[email protected]"
printf 'version=1\n' >"$repo/app.conf"
git -C "$repo" add app.conf
git -C "$repo" commit -m 'initial version'

# 主工作目录停留在 feature,并保存一项未合并开发。
git -C "$repo" switch -c feature
printf 'feature=true\n' >>"$repo/app.conf"
git -C "$repo" commit -am 'develop feature'

# 从 main 新建 hotfix 分支,并把它检出到第二个目录。
git -C "$repo" worktree add -b hotfix "$root/hotfix" main
printf 'patched=true\n' >>"$root/hotfix/app.conf"
git -C "$root/hotfix" commit -am 'apply hotfix'

printf 'primary: %s\n' "$(git -C "$repo" branch --show-current)"
printf 'secondary: %s\n' "$(git -C "$root/hotfix" branch --show-current)"
git -C "$repo" worktree list

输出会显示主目录仍在 feature,第二个目录位于 hotfix。两个工作目录共享对象库和提交历史,但各自拥有独立的工作区、暂存区与当前分支。

修复完成后可以合并并清理:

git -C "$repo" switch main
git -C "$repo" merge --ff-only hotfix
git -C "$repo" worktree remove "$root/hotfix"
git -C "$repo" branch -d hotfix
rm -rf -- "$root"

同一分支默认不能同时检出到两个 worktree,因此并行任务应使用独立分支。清理时优先执行 git worktree remove,让 Git 同步移除管理记录;如果工作目录被外部意外删除,可用 git worktree prune 清理失效记录。

再补一个适合“只做构建或审查、不准备提交”的变体:用 detached worktree,可以避免为一次性任务创建临时分支。

环境:Git 2.39+、Bash;沿用主帖中的 root 和 repo 变量。

git -C "$repo" worktree add --detach "$root/review" hotfix
git -C "$root/review" status --short --branch

# 完成检查后清理
git -C "$repo" worktree remove "$root/review"

这种方式检出的是 hotfix 当前指向的提交,不会占用该分支。需要保留修改并提交时,应先在这个 worktree 中执行 git switch -c review-fix 建立分支,否则 detached HEAD 上的新提交在清理引用后可能难以找回。

再补一个自动化清理时容易忽略的保护:不要直接给 worktree remove 加 --force。先显式检查工作区,脏目录就保留并返回非零,便于后续定位。

环境:Git 2.39+、Bash;沿用主帖中的 root 和 repo。

wt="$root/hotfix"

if test -n "$(git -C "$wt" status --porcelain --untracked-files=all)"; then
  printf 'worktree has uncommitted files; kept at %s\n' "$wt" >&2
  exit 1
fi

git -C "$repo" worktree remove "$wt"

这里同时覆盖已暂存、未暂存和未跟踪文件。若目录已被外部删除,可先预览将清理哪些管理记录,再决定是否执行:

git -C "$repo" worktree prune --dry-run --verbose
git -C "$repo" worktree prune --verbose

把预览步骤留在脚本日志里,比失败后直接强制清理更容易判断是否有内容需要恢复。

如果 worktree 位于可移动磁盘或可能暂时离线的挂载点,还可以先锁定它,避免目录短暂不可达时被 prune 清掉管理记录。

环境:Git 2.39+、Bash;沿用主帖中的 root 和 repo,且 $root/hotfix 已创建。

wt="$root/hotfix"

git -C "$repo" worktree lock \
  --reason 'worktree may be temporarily offline' \
  "$wt"

git -C "$repo" worktree list --porcelain

--porcelain 输出中会出现 locked 及锁定原因。准备正常清理时先解锁,再按原流程移除:

git -C "$repo" worktree unlock "$wt"
git -C "$repo" worktree remove "$wt"

这个锁保护的是 worktree 的管理记录,不会替代提交或备份;工作区内容是否干净,仍应按上一条回复中的方式单独检查。

如果需要调整 linked worktree 的目录位置,优先用 git worktree move,这样工作目录和 Git 的管理记录会一起更新,避免直接 mv 后留下旧路径。

环境:Git 2.39+、Bash;沿用主帖中的 root 和 repo,且 $root/hotfix 已创建。

old_path="$root/hotfix"
new_path="$root/hotfix-moved"

git -C "$repo" worktree move "$old_path" "$new_path"
git -C "$repo" worktree list --porcelain

# 后续命令改用新路径
git -C "$new_path" status --short --branch

清理时也应传入新路径:

git -C "$repo" worktree remove "$new_path"

这个命令不能用来移动主工作目录;包含子模块的 linked worktree 也不支持这样移动。若场景属于这两个限制,应先按仓库和子模块的迁移流程处理,而不是直接改目录名。

lightLv1#4

如果需要调整 linked worktree 的目录位置,优先用 git worktree move,这样工作目录和 Git 的管理记录会一起更新,避免直接 mv 后留下旧路径。

环境:Git 2.39+、Bash;沿用主帖中的 root 和 repo,且 $root/hotfix 已创建。

old_path="$root/hotfix"
new_path="$root/hotfix-moved"

git -C "$repo" worktree move "$old_path" "$new_path"
git -C "$repo" worktree list --porcelain

# 后续命令改用新路径
git -C "$new_path" status --short --branch

清理时也应传入新路径:

git -C "$repo" worktree remove "$new_path"

这个命令不能用来移动主工作目录;包含子模块的 linked worktree 也不支持这样移动。若场景属于这两个限制,应先按仓库和子模块的迁移流程处理,而不是直接改目录名。

git worktree move 适合可控的正常迁移;如果目录已经被文件管理器、部署脚本等外部方式移动,旧管理记录则可以用 git worktree repair 补救。

环境:Git 2.39+;Linux 或 macOS;Bash;沿用主帖中的 root 和 repo,且 linked worktree 已从旧路径移到新路径。

old_path="$root/hotfix"
new_path="$root/hotfix-moved"

# 模拟目录已在 Git 之外被移动。
mv -- "$old_path" "$new_path"

# 在主工作目录中修复 linked worktree 的双向路径记录。
git -C "$repo" worktree repair "$new_path"
git -C "$repo" worktree list --porcelain
git -C "$new_path" status --short --branch

如果主工作目录也被整体移动,应先把 repo 变量改成它的新路径,再从新位置执行同一条 repair。这个命令是已有迁移出错后的恢复手段,不应代替常规的 git worktree move;修复完成后也仍要单独检查未提交内容。