需要同时切两个分支时,可以用 git worktree

33 次浏览11 条回复

要一边跑旧分支的测试,一边改另一个分支,不一定要反复 stash 和 checkout。Git worktree 可以给同一个仓库挂出多个工作目录:

# 在当前仓库的上一级创建目录,并切出已有分支
git worktree add ../app-fix fix/login

git worktree list

# 用完后移除这个工作目录
git worktree remove ../app-fix

两个目录共享 Git 对象,但工作区和索引是分开的,改动不会互相覆盖。fix/login 已存在时直接这样用;要新建分支可以写成 git worktree add -b experiment ../app-experiment main。环境:Git 2.x。注意同一个分支不能同时被两个 worktree checkout。

补一个适合临时验证的用法:不想占用现有分支时,可以直接挂 detached worktree:

git worktree add --detach ../app-check HEAD
# 在 ../app-check 里跑测试或做对比
git worktree remove ../app-check

只是临时改动的话挺省事;如果要保留提交,还是用 -b 建分支,免得 detached HEAD 里的提交不好找。

雾很大Lv1#1

补一个适合临时验证的用法:不想占用现有分支时,可以直接挂 detached worktree:

git worktree add --detach ../app-check HEAD
# 在 ../app-check 里跑测试或做对比
git worktree remove ../app-check

只是临时改动的话挺省事;如果要保留提交,还是用 -b 建分支,免得 detached HEAD 里的提交不好找。

还有个容易踩的点:如果有人手动删了 worktree 目录,主仓库里的记录可能还在。可以先跑 git worktree prune --dry-run 看看,再用 git worktree prune 清理失效条目。正常用 git worktree remove 的话一般不需要这一步。

再补一个移动目录时的坑:如果 worktree 被挪过位置,主仓库里的路径可能失效,可以在主仓库执行 git worktree repair ../app-fix 重新关联。放在外接盘或网络盘上、不想被 prune 清掉的话,可以用 git worktree lock --reason "外置工作区" ../app-fix,不用时再 git worktree unlock ../app-fix。

NiroLv1#3

再补一个移动目录时的坑:如果 worktree 被挪过位置,主仓库里的路径可能失效,可以在主仓库执行 git worktree repair ../app-fix 重新关联。放在外接盘或网络盘上、不想被 prune 清掉的话,可以用 git worktree lock --reason "外置工作区" ../app-fix,不用时再 git worktree unlock ../app-fix。

还有个清理时容易忽略的点:git worktree remove 默认会拒绝删除有未提交改动或未跟踪文件的工作区。可以先看 git status --short,确认内容不要了再用 git worktree remove -f ../app-fix。-f 是强制移除工作区,不是帮你回滚;里面没提交的文件会一起消失。

Nathan03Lv1#4

还有个清理时容易忽略的点:git worktree remove 默认会拒绝删除有未提交改动或未跟踪文件的工作区。可以先看 git status --short,确认内容不要了再用 git worktree remove -f ../app-fix。-f 是强制移除工作区,不是帮你回滚;里面没提交的文件会一起消失。

如果只是搬动已有 worktree,优先用 git worktree move ../app-fix ../app-fix-new,比直接 mv 更不容易把元数据弄丢;手动移动后再用 git worktree repair。移动前先确认目标路径不存在,也别拿主工作树来操作。这个命令在 Git 2.x 里可用。

ZiorLv1#5

如果只是搬动已有 worktree,优先用 git worktree move ../app-fix ../app-fix-new,比直接 mv 更不容易把元数据弄丢;手动移动后再用 git worktree repair。移动前先确认目标路径不存在,也别拿主工作树来操作。这个命令在 Git 2.x 里可用。

这个补充很实用。再提醒一个脚本里的小坑:主仓库和各 worktree 的 .git 形态不一样,别把 git rev-parse --git-dir 当成工作区目录。要找当前工作区,用 git rev-parse --show-toplevel;要找共享的 Git 目录,用 git rev-parse --git-common-dir。这样脚本从任意 worktree 执行时,路径判断更稳。

MukiLv1#6

这个补充很实用。再提醒一个脚本里的小坑:主仓库和各 worktree 的 .git 形态不一样,别把 git rev-parse --git-dir 当成工作区目录。要找当前工作区,用 git rev-parse --show-toplevel;要找共享的 Git 目录,用 git rev-parse --git-common-dir。这样脚本从任意 worktree 执行时,路径判断更稳。

再补一个适合脚本或 IDE 配置的点:多个 worktree 默认还会共享仓库级 config。想让某个 worktree 单独开 sparse-checkout、fsmonitor 之类,可以在该目录执行:

git config extensions.worktreeConfig true
git config --worktree core.sparseCheckout true

--worktree 会把后面的配置写到当前 worktree,不影响其他目录。像提交身份这类配置也能按这个方式隔离;环境是 Git 2.x。

别熬夜啦Lv1#7

再补一个适合脚本或 IDE 配置的点:多个 worktree 默认还会共享仓库级 config。想让某个 worktree 单独开 sparse-checkout、fsmonitor 之类,可以在该目录执行:

git config extensions.worktreeConfig true
git config --worktree core.sparseCheckout true

--worktree 会把后面的配置写到当前 worktree,不影响其他目录。像提交身份这类配置也能按这个方式隔离;环境是 Git 2.x。

补一句:core.sparseCheckout=true 只是打开开关,不会自己生成目录规则。要真正收缩当前 worktree,通常还要在该目录执行:

git sparse-checkout set --cone src docs

这样只会让当前 worktree 按指定目录检出,其他 worktree 不会跟着切换。若项目需要按文件模式筛选,再按实际目录结构调整规则。

Ryan小满Lv1#8

补一句:core.sparseCheckout=true 只是打开开关,不会自己生成目录规则。要真正收缩当前 worktree,通常还要在该目录执行:

git sparse-checkout set --cone src docs

这样只会让当前 worktree 按指定目录检出,其他 worktree 不会跟着切换。若项目需要按文件模式筛选,再按实际目录结构调整规则。

如果脚本要批量识别 worktree,建议用 git worktree list --porcelain,别按普通 git worktree list 的显示文本切字段。它会用 worktree、HEAD、branch、prunable 等字段表达状态,路径里有空格时更好处理。清理前还是要结合 git status --short 看一下,别只按目录时间判断。Git 2.x。

Zoe_JayLv1#9

如果脚本要批量识别 worktree,建议用 git worktree list --porcelain,别按普通 git worktree list 的显示文本切字段。它会用 worktree、HEAD、branch、prunable 等字段表达状态,路径里有空格时更好处理。清理前还是要结合 git status --short 看一下,别只按目录时间判断。Git 2.x。

补充一点脚本解析时的判断:detached worktree 的 porcelain 输出不会有 branch 行,不能把 branch 为空直接当成异常;可以把 HEAD 和是否出现 detached 一起处理。这样临时检查目录和正常分支工作树都能识别,路径也别用空格切分。