git本身不提供切换分支前的拦截钩子,pre-checkout仅在checkout后触发且不适用于git switch;最可行方案是用wrapper脚本替代git命令,在执行checkout/switch前主动备份工作区状态。

git checkout 或 git switch 触发前怎么拦截
Git 本身不提供“切换分支前钩子”,pre-checkout 钩子只在 git checkout 执行后、工作区更新前触发,且不适用于 git switch(它压根不调用该钩子)。真正能稳定拦截分支切换动作的,只有 git update-ref 和 git symbolic-ref 的间接路径——但它们太底层,不可靠。最可行的方案是:**放弃拦截,改用 wrapper 命令主动触发备份**。
也就是说,你不该指望 Git 自己“在切换前自动做某事”,而是让日常使用的 git checkout / git switch 命令实际执行的是你写的封装脚本。
- 把原生
git二进制重命名为git-real - 新建
gitshell 脚本,放在$PATH更高优先级位置(如~/bin/git) - 脚本识别
checkout和switch子命令,先运行备份逻辑,再调用git-real
备份工作区状态该存哪些内容
“备份当前工作区状态”不是指打 commit,而是保留未暂存/未提交的修改、stashed 内容、甚至 index 状态,以便切回时还原。重点要捕获:
-
git status --porcelain输出为空?说明干净,跳过备份 - 否则,用
git stash push -m "auto-backup-$(date +%s)"存当前修改(含 untracked 文件需加-u) - 注意:如果已有 stash,
git stash会追加;但自动脚本应避免干扰用户原有 stash,建议用git stash store+ 自定义 ref(如refs/auto-backup/branchname)隔离存储 - index 状态无法直接 stash,若需精确还原,可提前
git update-index --refresh并记录git ls-files --stage快照(较少必要)
如何避免重复备份或覆盖已有备份
每次切换都 stash 会导致 stash 列表膨胀,且容易误 pop。关键控制点:
- 只对目标分支(即即将切换到的分支)做一次备份,而不是每次
git checkout都无脑 stash - 用
git config --get core.worktree或pwd结合当前 repo 路径生成唯一 key,例如:backup-ref = refs/auto-backup/$(basename $(pwd | sed 's|/|_|g'))_$(git rev-parse --abbrev-ref HEAD) - 检查该 ref 是否已存在:
git show-ref --verify --quiet refs/auto-backup/xxx,存在则跳过 stash,直接切换 - 切换完成后,可选地自动 pop 对应 stash(需判断目标分支是否与上次备份分支一致,否则不 pop)
git switch 不走 alias,wrapper 脚本必须覆盖所有入口
git switch 是独立命令,不会被 git config alias.switch 覆盖;alias 也绕不过 shell wrapper。所以必须确保你的 wrapper 脚本能处理所有可能入口:
- 显式调用:
git checkout main、git switch feat/login - 隐式调用:IDE(VS Code、JetBrains)内置 Git 操作、CI 脚本中的
git checkout - 某些 Git GUI 工具直接调用
git二进制,不走 shell —— 这类无法拦截,只能接受“GUI 场景不备份”的事实 - 务必测试
git -C /path/to/repo switch develop这类带-C参数的调用,wrapper 中要用shift正确解析参数
真正的难点不在脚本逻辑,而在“什么时候不该备份”:比如刚 git pull 后立即 switch,工作区其实是干净的;或者用户明确加了 --no-track 或 --detach,意图就是丢弃当前状态。这些边界得靠 git status --porcelain 和参数解析共同判断,不能只看子命令名。











