前言
相信每个用 git 的人都经历过这种绝望时刻:
bash
# 本想切个分支,结果...
git checkout .
# 工作区所有改动瞬间消失,欲哭无泪
# 或者...
git reset --hard HEAD
# 敲完回车才想起来还有没 commit 的代码
尤其是 git checkout 和 git reset --hard 这种毁灭性操作,一旦执行就没有后悔药。每次敲回车前都像在玩俄罗斯轮盘赌。
本文教你一个简单的方法:给这两个高危命令加一层安全检查,工作区不干净就直接拦截,从根源上杜绝手滑。
原理
核心思路是覆盖原命令,在执行前先检查工作区和暂存区是否有未提交的改动:
bash
git diff --exit-code # 检查工作区 vs 暂存区有无差异
git diff --cached --exit-code # 检查暂存区 vs 上次 commit 有无差异
两者都返回 exit code 0(无差异)才放行,否则报错退出。
实现
将以下代码添加到你的 .zshrc 或 .bashrc 中(或者像我一样单独放在 ~/.git-aliases.zsh 再 source):
zsh
# 安全切换分支,有已跟踪文件改动则拦截
unalias gco 2>/dev/null
gco() {
if git diff --exit-code > /dev/null && git diff --cached --exit-code > /dev/null; then
git checkout "$@"
else
echo "⛔ 工作区不干净,请先 stash 或 commit"
return 1
fi
}
# 安全 git reset --hard,有已跟踪文件改动则拦截
unalias grh 2>/dev/null
grh() {
if git diff --exit-code > /dev/null && git diff --cached --exit-code > /dev/null; then
git reset --hard "$@"
else
echo "⛔ 工作区不干净,禁止 reset --hard"
return 1
fi
}
重新加载配置后就能立即生效:
bash
source ~/.zshrc
使用效果
工作区有改动时:
bash
$ gco main
⛔ 工作区不干净,请先 stash 或 commit
工作区干净时正常执行:
bash
$ gco main
Switched to branch 'main'
注意点
- 只保护已跟踪文件 --- 新增的未跟踪文件(untracked files)不会触发拦截,因为
git diff默认只关注 tracked files。如果你想连 untracked files 一起保护,可以加上git ls-files --others --exclude-standard检查。 gco只拦截裸 checkout --- 如果 checkout 到某个分支且工作区干净则放行。如果 checkout 的分支与当前分支有冲突,git 本身会报错,不会覆盖你的文件。- 建议全量覆盖 --- 如果你习惯用
git checkout原生命令,可以用 alias 把git checkout也覆盖掉:
zsh
alias git='safe_git'
safe_git() {
if [[ "$1" == "checkout" || "$1" == "reset" ]]; then
# 同样的安全检查...
fi
command git "$@"
}
不过我个人建议直接用函数覆盖 gco 和 grh 就好,保留原生 git 命令以备不时之需。
进阶:连 untracked files 一起保护
如果你连新增文件也想保护,改一下条件判断:
zsh
gco() {
if git diff --exit-code > /dev/null && git diff --cached --exit-code > /dev/null && [[ -z "$(git ls-files --others --exclude-standard)" ]]; then
git checkout "$@"
else
echo "⛔ 工作区不干净(含未跟踪文件),请先 stash 或 commit"
return 1
fi
}
写在最后
这层保护我用了两年多,每天几十次 gco / grh 操作,再也没手滑丢过代码。配置简单、零依赖,强烈建议每一个开发者都安排上。
保护自己最好的方式,就是不要给自己犯错的机会。
如果你也有类似的 git 安全技巧,欢迎在评论区分享 👇