用脚本打造更安全的 Git 工作流
在日常使用 Git 管理知识库或代码仓库时,大多数人都会使用类似下面的三连命令:
git add .
git commit -m "update"
git push
或者在开始工作前执行:
git pull
这种方式虽然简单,但实际上存在不少隐患。例如:
-
本地工作区有未提交修改时直接
pull -
远程仓库已经更新,但本地没有同步就直接
push -
本地和远程分支已经分叉(diverged)却继续操作
-
未跟踪文件在
pull时导致目录混乱 -
自动生成的文件被误提交
这些问题在 多设备同步(电脑 + 手机)、Obsidian 知识库、个人博客仓库 这种场景下尤其容易出现。
为了让 Git 工作流更安全、更可控,可以将常用操作封装成脚本,在执行 pull 和 push 时自动进行检查。
本文介绍两个简单但非常实用的脚本:
-
vaultpullsafe:安全拉取 -
vaultpushsafe:安全推送
它们能够在执行操作前自动检查仓库状态,从而避免很多常见问题。
一、安全拉取脚本¶
安全拉取脚本的目标是:
只有在仓库状态安全的情况下才允许执行 pull。
主要检查内容包括:
-
当前目录是否为 Git 仓库
-
工作区是否干净
-
是否存在未跟踪文件
-
本地和远程分支关系
-
是否存在分叉
脚本如下:
#!/bin/bash
set -e
echo "==> 检查当前目录是否为 Git 仓库"
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
echo "错误:当前目录不是 Git 仓库"
exit 1
}
echo "==> 当前分支:$(git branch --show-current)"
echo "==> 检查工作区是否干净"
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "错误:当前工作区有未提交修改,请先提交或清理后再拉取"
git status --short
exit 1
fi
if [ -n "$(git ls-files --others --exclude-standard)" ]; then
echo "错误:存在未跟踪文件,为避免 pull 时混乱,已停止"
git status --short
exit 1
fi
echo "==> 获取远程最新信息"
git fetch origin
BRANCH=$(git branch --show-current)
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse "origin/$BRANCH")
BASE=$(git merge-base @ "origin/$BRANCH")
if [ "$LOCAL" = "$REMOTE" ]; then
echo "==> 本地与远程一致,无需拉取"
exit 0
fi
if [ "$LOCAL" = "$BASE" ]; then
echo "==> 远程有更新,开始执行 git pull --rebase"
git pull --rebase origin "$BRANCH"
echo "==> 拉取完成"
git status
exit 0
fi
if [ "$REMOTE" = "$BASE" ]; then
echo "==> 本地领先远程,没有必要 pull"
git status
exit 0
fi
echo "错误:本地与远程已分叉(diverged),不能自动安全拉取"
echo "请手动检查:"
echo " git status"
echo " git log --oneline --graph --decorate --all -20"
exit 1
二、安全推送脚本¶
安全推送脚本的目标是:
只有在远程状态安全、且本地确实存在改动时才执行 push。
主要检查逻辑:
-
当前目录是否为 Git 仓库
-
是否存在需要提交的改动
-
远程是否领先本地
-
本地与远程是否分叉
-
提交后远程是否发生变化
脚本如下:
#!/bin/bash
set -e
MSG="$1"
if [ -z "$MSG" ]; then
MSG="update $(date '+%Y-%m-%d %H:%M:%S')"
fi
echo "==> 检查当前目录是否为 Git 仓库"
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
echo "错误:当前目录不是 Git 仓库"
exit 1
}
BRANCH=$(git branch --show-current)
echo "==> 当前分支:$BRANCH"
echo "==> 检查是否存在改动"
if git diff --quiet && git diff --cached --quiet && [ -z "$(git ls-files --others --exclude-standard)" ]; then
echo "==> 没有可提交的改动,停止操作"
exit 0
fi
echo "==> 先获取远程最新信息"
git fetch origin
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse "origin/$BRANCH")
BASE=$(git merge-base @ "origin/$BRANCH")
if [ "$LOCAL" = "$BASE" ] && [ "$REMOTE" != "$BASE" ]; then
echo "错误:远程领先本地,请先执行安全拉取"
echo "建议运行:vaultpullsafe"
exit 1
fi
if [ "$LOCAL" != "$BASE" ] && [ "$REMOTE" != "$BASE" ] && [ "$LOCAL" != "$REMOTE" ]; then
echo "错误:本地与远程已分叉,不能直接推送"
echo "请先手动检查:"
echo " git status"
echo " git log --oneline --graph --decorate --all -20"
exit 1
fi
echo "==> 查看当前变更概况"
git status --short
echo "==> 加入暂存区"
git add .
echo "==> 创建提交"
git commit -m "$MSG"
echo "==> 再次检查远程状态"
git fetch origin
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse "origin/$BRANCH")
BASE=$(git merge-base @ "origin/$BRANCH")
if [ "$REMOTE" != "$BASE" ] && [ "$LOCAL" != "$REMOTE" ]; then
echo "错误:提交后发现远程又更新了,已停止 push"
echo "请先运行:vaultpullsafe"
exit 1
fi
echo "==> 推送到远程"
git push origin "$BRANCH"
echo "==> 推送完成"
git status
三、脚本的核心原理¶
这两个脚本的关键在于判断 本地与远程的关系。
主要通过三个变量:
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse "origin/$BRANCH")
BASE=$(git merge-base @ "origin/$BRANCH")
含义分别是:
-
LOCAL:本地当前提交 -
REMOTE:远程分支提交 -
BASE:两者的共同祖先
根据三者关系可以判断:
| 状态 | 含义 |
|---|---|
| LOCAL = REMOTE | 本地与远程一致 |
| LOCAL = BASE | 本地落后远程 |
| REMOTE = BASE | 本地领先远程 |
| 三者都不同 | 本地与远程已分叉 |
这样脚本就可以决定是否允许继续操作。
四、如何使用¶
假设脚本命名为:
vaultpullsafe
vaultpushsafe
日常工作流程就变成:
开始工作前:
vaultpullsafe
完成编辑后:
vaultpushsafe "更新知识库"
如果不写提交信息,脚本会自动生成:
update YYYY-MM-DD HH:MM:SS
五、这种方式的优势¶
相比传统 Git 操作,这种方式具有明显优势:
1 自动安全检查
避免在以下情况误操作:
-
本地有未提交修改
-
远程已经更新
-
分支已经分叉
2 减少冲突
脚本默认使用:
git pull --rebase
可以减少不必要的 merge commit。
3 操作更简单
日常只需要两个命令:
vaultpullsafe
vaultpushsafe
4 非常适合知识库仓库
例如:
-
Obsidian 笔记
-
博客仓库
-
多设备同步仓库
六、总结¶
Git 本身是一个非常强大的版本控制工具,但默认命令往往对新手不够友好。
通过简单的脚本封装,可以实现:
-
自动检查仓库状态
-
自动判断分支关系
-
自动阻止危险操作
-
自动化常见工作流程
最终让 Git 的使用更加稳定、可控。
在日常使用中,你只需要记住两个命令:
vaultpullsafe
vaultpushsafe
就可以完成一整套安全的 Git 同步流程。