🚀 第十二章:高级技巧 —— Git 的隐藏大招
git stash—— 临时保存工作
场景:你正在开发新功能,突然老板说"生产环境有个紧急 bug,赶紧修!"。你手头的代码改了一半,不想提交一个半成品。怎么办?git stash 来救你!
bash
# 把当前修改"藏起来"
git stash
Saved working directory and index state WIP on main: f4e5d6c feat: 添加欢迎语
# 工作区变干净了
git status
On branch main
nothing to commit, working tree clean
# 现在可以安心修 bug 了...
# 修完 bug 并提交后,恢复之前的工作
git stash pop
On branch main
Changes not staged for commit:
modified: index.html
# 查看所有 stash 记录
git stash list
stash@{0}: WIP on main: f4e5d6c feat: 添加欢迎语
# 带描述的 stash(推荐!)
git stash push -m "正在开发用户头像功能"
git stash list
stash@{0}: On main: 正在开发用户头像功能
# 恢复指定的 stash(不删除 stash 记录)
git stash apply stash@{0}
# 恢复并删除 stash 记录
git stash pop stash@{0}
# 删除所有 stash
git stash cleargit cherry-pick—— 精确"移植"提交
有时候你只想把某一个提交"搬"到另一个分支,而不是合并整个分支。这时候 cherry-pick 就是你的神器。
bash
# 场景:feature/实验 功能分支上有一个紧急修复,你想单独把它搬到 main
# 先找到那个提交的哈希值
git log --oneline feature/实验
d4e5f6a feat: 实验性功能 A
c3d4e5f fix: 修复了一个严重 bug
b2c3d4e feat: 实验性功能 B
a1b2c3d feat: 初始化实验分支
# 切到 main,cherry-pick 那个修复
git switch main
git cherry-pick c3d4e5f
[main e5f6a7b] fix: 修复了一个严重 bug
Date: Thu Jun 20 15:30:00 2025 +0800
1 file changed, 2 insertions(+), 1 deletion(-)
# 也可以一次 cherry-pick 多个提交
git cherry-pick a1b2c3d..c3d4e5f
# cherry-pick 但不自动提交(让你先检查)
git cherry-pick --no-commit c3d4e5fgit bisect—— 二分查找定位 bug
这个命令厉害了!如果你知道 100 个提交前代码是好的,现在有 bug,bisect 用二分查找帮你快速定位是哪个提交引入了 bug。
bash
# 开始二分查找
git bisect start
# 标记当前版本有 bug
git bisect bad
# 标记某个旧版本是好的
git bisect good a1b2c3d
Bisecting: 50 revisions left to test after this (roughly 6 steps)
[f5a6b7c...] feat: 添加搜索功能
# Git 会自动 checkout 到中间的提交
# 测试这个版本...
# 如果好的:
git bisect good
Bisecting: 25 revisions left to test after this (roughly 5 steps)
# 如果有 bug:
git bisect bad
Bisecting: 12 revisions left to test after this (roughly 4 steps)
# 重复几次后,Git 会告诉你:
a7b8c9d is the first bad commit
commit a7b8c9d
Author: 小红 <xiaohong@example.com>
Date: Thu Jun 20 14:00:00 2025 +0800
feat: 添加购物车功能
:040000 040000 abc123 def456 M cart.js
# 找到 bug 的"元凶"了!结束 bisect
git bisect resetgit blame—— 这行代码谁写的?
bash
# 查看文件每一行的最后修改者
git blame index.html
a1b2c3d4 (小明 2025-06-20 10:00:00 +0800 1) <!DOCTYPE html>
a1b2c3d4 (小明 2025-06-20 10:00:00 +0800 2) <html>
a1b2c3d4 (小明 2025-06-20 10:00:00 +0800 3) <head>
f4e5d6c7 (小明 2025-06-20 10:30:00 +0800 4) <title>我的第一个网站</title>
a1b2c3d4 (小明 2025-06-20 10:00:00 +0800 5) </head>
a1b2c3d4 (小明 2025-06-20 10:00:00 +0800 6) <body>
d4e5f6a7 (小红 2025-06-20 11:00:00 +0800 7) <h1>欢迎来到我们的网站!</h1>
f4e5d6c7 (小明 2025-06-20 10:30:00 +0800 8) <p>Hello, Git!</p>
a1b2c3d4 (小明 2025-06-20 10:00:00 +0800 9) </body>
a1b2c3d4 (小明 2025-06-20 10:00:00 +0800 10) </html>
# 查看特定行范围
git blame -L 5,8 index.htmlgit grep—— 在代码中搜索
bash
# 搜索代码内容(比 grep 快,因为利用了 Git 的索引)
git grep "Hello"
index.html:7: <h1>Hello, Git!</h1>
# 搜索并统计每个文件的匹配次数
git grep -c "class"
index.html:2
style.css:5
# 搜索某个特定提交中的内容
git grep "Hello" a1b2c3dGit 别名 —— 给命令起外号
有些 Git 命令太长了,打起来费劲。设置别名让你的效率翻倍:
bash
# 常用别名设置
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.lg "log --oneline --graph --all --decorate"
git config --global alias.last "log -1 HEAD"
git config --global alias.unstage "restore --staged"
git config --global alias.aliases "config --get-regexp alias"
# 使用别名
git st # = git status
git co main # = git checkout main
git lg # = 好看的提交历史Git Hooks —— 自动化脚本
Git Hooks 是在特定事件发生时自动执行的脚本。比如每次提交前自动检查代码格式,每次推送前自动运行测试。
bash
# 查看可用的 hooks
ls .git/hooks/
applypatch-msg.sample pre-commit.sample pre-rebase.sample
commit-msg.sample pre-merge-commit.sample prepare-commit-msg.sample
fsmonitor-watchman.sample pre-push.sample update.sample
# 创建一个 pre-commit hook(提交前运行检查)
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
# 检查是否有未解决的冲突标记
if git diff --cached --diff-filter=ACM | grep -q '<<<<<<'; then
echo "错误:发现未解决的冲突标记!请先解决冲突。"
exit 1
fi
# 检查是否有调试语句
if git diff --cached --diff-filter=ACM | grep -qE 'console\.log|debugger|print\('; then
echo "警告:代码中可能包含调试语句。"
echo "如果确认无误,请使用 git commit --no-verify 跳过检查。"
exit 1
fi
EOF
# 设置可执行权限
chmod +x .git/hooks/pre-commit📝 备注: .git/hooks/目录下的脚本不会被提交到仓库。如果想让团队共享 hooks,可以使用pre-commit这个工具,或者把 hooks 脚本放在项目目录中,通过符号链接来使用。
Git Submodules —— 仓库里的仓库
有时候你的项目依赖另一个 Git 仓库(比如一个公共库)。submodule 让你在一个仓库里嵌套另一个仓库。
bash
# 添加子模块
git submodule add git@github.com:xiaoming/公共工具库.git libs/utils
Cloning into '/home/xiaoming/我的第一个网站/libs/utils'...
remote: Enumerating objects: 42, done.
...
Adding existing repo at 'libs/utils' to the index
# 查看子模块状态
git submodule status
a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 libs/utils (heads/main)
# 克隆包含子模块的仓库
git clone --recurse-submodules git@github.com:小明/我的第一个网站.git
# 如果已经克隆了但忘了 --recurse-submodules
git submodule update --init --recursive