git checkout 拆成两条命令:Git 2.23 的 git switch / git restore 选项行为与迁移对照
Git 2.23.0(2019-08-16)引入 git switch 与 git restore,把原本 git checkout 一肩挑的「切换分支」和「检出文件内容」两类职责拆开。
git checkout 的三种语义
同一个命令承担三件事:
git checkout # 切分支
git checkout -b # 创建并切换到新分支
git checkout --
# 检出文件/路径:丢弃工作区改动,
# 或从某个 commit/tag 取文件覆盖
问题在于第三种用法和第一种的动词形式几乎一样,误操作(本想切分支却覆盖了工作区)在命令行补全下很容易发生。拆开的两条命令各自只做一件事。
git switch —— 只管分支切换
git switch [] [--no-guess]
git switch [] --detach []
git switch [] (-c|-C) []
git switch [] --orphan
关键行为与选项:
- 切分支不要求干净的索引和工作区;但若操作会导致本地改动丢失则中止,除非给
--discard-changes或--merge。 -c/--create:创建并切换,是「事务性」的,等价于git branch+git switch;失败时(例如该分支已被其它 worktree 占用)分支不会被创建/重置。-C/--force-create:类似-c,但若分支已存在则重置到 ``,等价于git branch -f+git switch。-d/--detach []:切到某个 commit 做检查/实验(分离 HEAD)。@{-N}可引用第 N 次用switch/checkout切到的分支/提交;-等同于@{-1},用于在两条分支间快速往返或撤销误切。--guess/--no-guess:`` 本地不存在但在某个远端恰有同名跟踪分支时,等价于git switch -c --track /。--guess是默认值,可用checkout.guess=false关掉。-m/--merge:有会冲突的本地改动时先 autostash,切换后重新应用;仍有冲突则存入 stash 列表。-f/--discard-changes:即使索引/工作区与 HEAD 不同也继续,丢弃本地改动。--orphan:创建无父的 unborn 分支并移除所有已跟踪文件。-t/--track[ (direct|inherit) ]:建新分支时设置 upstream。- 还有
--ignore-other-worktrees、--recurse-submodules、-q/--quiet、--progress。
git restore —— 只处理路径内容
git restore [] [--source=] [--staged] [--worktree] [--]
...
git restore [] [--source=] [--staged] [--worktree] --pathspec-from-file= [--pathspec-file-nul]
git restore (-p|--patch) [] [--source=] [--staged] [--worktree] [--] [
...]
关键行为与选项:
- 用 restore source 的内容恢复工作区指定路径;若路径已跟踪但在 source 中不存在,会被删除以匹配 source。
- 默认恢复位置取决于选项:给
--staged时从 HEAD 恢复索引,否则从索引恢复工作区。用--source=从其它 commit/branch/tag 恢复。 -W/--worktree与-S/--staged指定恢复位置;两者都不给默认恢复工作区;两者都给则同时恢复索引和工作区。-s/--source=:从给定 tree 恢复;该选项不支持与--ours/--theirs/-m同用(后三者只用于从索引恢复)。--ours/--theirs、-m/--merge、--conflict=(merge|diff3|zdiff3)、--ignore-unmerged:处理未合并(冲突)路径。-p/--patch交互式挑 hunk;--pathspec-from-file/--pathspec-file-nul。--overlay/--no-overlay:默认no-overlay,会删除--source中没有的已跟踪文件以精确匹配 source。- 另有
--recurse-submodules、--ignore-skip-worktree-bits、-q/--quiet。
迁移对照
| 旧写法 | 新写法 |
|---|---|
git checkout | git switch |
git checkout -b | git switch -c |
git checkout -- (丢弃工作区改动) | git restore |
git checkout -- | git restore --source= |
git reset (取消暂存) | git restore --staged |
git checkout . / git checkout -- . | git restore . |
官方示例
switch 侧:
git switch master
git switch -m mytopic # 本地改动被 autostash 后带过去
git switch - # 回到上一个分支
git switch -c fixup HEAD~3
git switch --detach HEAD~3
git switch -c good-surprises # 给实验成果命名
restore 侧:
git restore --source master~2 Makefile
git restore hello.c
git restore '*.c'
git restore .
git restore :/
git restore --staged hello.c
git restore -s@ -SW hello.c # 等价 --source=HEAD --staged --worktree
为什么 git 不删掉 checkout
git checkout 没有被废弃:
- 脚本与历史兼容。大量现有脚本、CI 配置、文档都在用它。
checkout仍能一条命令同时操作索引与工作区,而restore需要显式写--staged --worktree。checkout还能直接切到 commit 并附带路径等混合操作,switch与restore都不覆盖这种组合。
新老 Git 的分界线是 2.23.0:这之前没有 switch/restore,写脚本或 CI 要判版本,否则在旧环境上会直接报未知命令。
相关配置项
checkout.defaultRemotecheckout.guesscheckout.workers:默认 1,小于 1 时按逻辑核数checkout.thresholdForParallelism:默认 100 个文件
文档:git-switch 、git-restore。