Skip to content
Git
高级操作

高级操作

本节涵盖 Git 的高级功能:stash、bisect、tag、worktree、submodule、hooks 等。


stash(暂存工作区)

将未提交的修改"暂存"起来,获得干净的工作区。

# 暂存当前修改
git stash
git stash push -m "WIP: refactoring login"

# 暂存所有(包括未跟踪的文件)
git stash -u
git stash --include-untracked

# 查看 stash 列表
git stash list
# stash@{0}: On main: WIP: refactoring login
# stash@{1}: On feature: Half-done work

# 恢复最近一次 stash(不删除 stash 记录)
git stash apply
git stash apply stash@{1}

# 恢复最近一次 stash(删除 stash 记录)
git stash pop

# 查看 stash 内容
git stash show -p
git stash show -p stash@{1}

# 删除 stash
git stash drop stash@{0}
git stash clear                # 清空所有

# 从 stash 创建分支
git stash branch <branch_name> stash@{0}

stash 典型场景

# 场景:开发到一半,需要紧急修复 bug
git stash -u                   # 暂存当前工作
git checkout main
git checkout -b hotfix
# ... 修复、提交 ...
git checkout feature
git stash pop                  # 恢复之前的工作

💡 最佳实践:给 stash 写描述信息(git stash push -m "message"),否则一段时间后很难回忆起它的内容。


bisect(二分查找)

用二分法定位引入 bug 的提交。自动化排查利器。

# 启动二分查找
git bisect start

# 标记当前版本为 bad(有问题)
git bisect bad HEAD

# 标记已知正常版本为 good
git bisect good v1.0
# Git 会自动 checkout 中间的某个提交

# 测试后标记
git bisect good                 # 这个版本没问题
git bisect bad                  # 这个版本有问题

# 重复 good/bad,直到定位到引入 bug 的第一个 commit

# 结束 bisect
git bisect reset               # 回到原始分支

# 自动化 bisect(推荐)
git bisect start HEAD v1.0
git bisect run ./test_script.sh  # 脚本返回 0=good, 非 0=bad

自动化 bisect 示例

# test_script.sh
#!/bin/bash
npm test -- --testPathPattern="failing_test" > /dev/null 2>&1
exit $?

# 自动跑
git bisect start HEAD v2.0
git bisect run ./test_script.sh
# Git 自动二分查找,输出第一个 bad commit
git bisect reset

tag(标签)

# 轻量标签(只记录引用)
git tag v1.0

# 注释标签(推荐——包含元数据)
git tag -a v1.0 -m "Release version 1.0"
git tag -a v1.0 -m "Release v1.0" <commit_hash>   # 为历史提交打标签

# 查看标签
git tag                         # 列出所有
git tag -l "v1.*"               # 通配符过滤
git show v1.0                   # 查看标签详情

# 推送标签
git push origin v1.0            # 推送单个
git push --tags                 # 推送所有本地标签

# 删除标签
git tag -d v1.0                 # 本地
git push origin --delete v1.0  # 远程

# 基于标签创建分支
git checkout -b hotfix-v1.0 v1.0

describe(描述位置)

git describe <ref>
# 输出:v1.0-5-gabc1234
#       │    │  │
#       │    │  └── hash 前缀(g = git)
#       │    └── 距离 tag 有 5 个提交
#       └── 最近的 tag

# 当 ref 正好是一个 tag 时,只输出 tag 名称
git describe HEAD
# → v2.0

worktree(多工作目录)

在同一个仓库中同时检出多个分支到不同目录。

# 创建新的 worktree
git worktree add ../project-hotfix hotfix
# 在 ../project-hotfix 目录中检出 hotfix 分支

# 创建并检出到新分支
git worktree add -b new-feature ../project-feature main

# 查看所有 worktree
git worktree list

# 删除 worktree
git worktree remove ../project-hotfix
# 或先手动删除目录,然后:
git worktree prune

💡 典型场景:正在 feature 分支开发,需要紧急切换到另一个分支修 bug,又不想 stash。直接用 worktree 在另一个目录操作,互不干扰。


submodule(子模块)

在仓库中引用另一个 Git 仓库。

# 添加子模块
git submodule add <url> <path>
git submodule add git@github.com:lib/common.git libs/common

# 克隆带子模块的仓库
git clone --recurse-submodules <url>
# 已克隆但忘加 --recurse-submodules 时:
git submodule update --init --recursive

# 更新子模块到最新提交
git submodule update --remote

# 查看子模块状态
git submodule status
git submodule foreach git status   # 在每个子模块中执行命令

🚨 陷阱:子模块会锁死在特定 commit 上。主仓库 git pull 不会自动更新子模块,需要手动 git submodule update

subtree(子模块的替代方案)

# 添加外部仓库为子目录
git subtree add --prefix=libs/common <url> main --squash

# 拉取更新
git subtree pull --prefix=libs/common <url> main --squash

# 推送修改回上游
git subtree push --prefix=libs/common <url> main
特性 submodule subtree
外部仓库独立性 独立引用 直接合并到主仓库
主仓库大小 小(只存引用) 大(存完整代码)
克隆复杂度 需要额外步骤 普通 clone 即可
修改并回馈上游 较复杂 相对简单

hooks(钩子)

Git 在执行特定操作前后触发的脚本。

# 客户端钩子(在 .git/hooks/ 下)
钩子 触发时机 典型用途
pre-commit commit 前 lint、格式化检查
commit-msg 编辑 commit 信息后 验证 commit 信息格式
pre-push push 前 运行测试
pre-rebase rebase 前 禁止 rebase 特定分支
post-checkout checkout 后 安装依赖
post-merge merge 后 依赖更新提醒
# 示例:pre-commit 钩子(自动 lint)
# .git/hooks/pre-commit
#!/bin/sh
npm run lint
if [ $? -ne 0 ]; then
    echo "Lint failed. Commit aborted."
    exit 1
fi

# 启用钩子(文件需要可执行权限)
chmod +x .git/hooks/pre-commit

共享 hooks(husky)

由于 .git/hooks 不在版本控制中,团队通常用工具管理:

# Husky(Node.js 项目)
npm install husky --save-dev
npx husky install
npx husky add .husky/pre-commit "npm run lint"
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'

reflog 深度用法

恢复误删分支

# 分支被删除后,通过 reflog 找到其最后的 commit
git reflog
# 找到删除前该分支的 HEAD 位置
git branch recovered_branch <hash>

恢复误 reset

git reflog
# HEAD@{0}: reset: moving to HEAD~5  ← 误操作
# HEAD@{1}: commit: Important work   ← 丢失的重要提交
git reset --hard HEAD@{1}

高级工具速查

工具 用途 核心命令
git archive 导出代码快照(不含 .git) git archive --format=tar HEAD
git bundle 离线传输仓库 git bundle create repo.bundle --all
git fsck 校验仓库完整性 git fsck --lost-found
git filter-repo 仓库历史重写(替代 filter-branch) git filter-repo --path secrets/ --invert-paths
git notes 给 commit 添加备注(不改 hash) git notes add -m "Reviewed" <hash>
git rebase --onto 将一段提交移植到新 base git rebase --onto <newbase> <oldbase> <branch>
git rerere 自动重用在冲突解决方法 git config --global rerere.enabled true
git blame 逐行查看修改者 git blame -L 1,50 <file>
git format-patch 生成补丁文件 git format-patch HEAD~3
git am 应用补丁 git am < patch.diff

rebase –onto 详解

# 场景:feature 分支基于 develop,现在要移到 main 上
# git rebase --onto <new-base> <upstream> <branch>

git rebase --onto main develop feature
# 将 feature 中不在 develop 上的提交,移植到 main 上
之前:
main:    A---B---C---D
                \
develop:         E---F
                      \
feature:               G---H

git rebase --onto main develop feature

之后:
main:    A---B---C---D---G'---H'  (feature)
                \
develop:         E---F

rerere(Reuse Recorded Resolution)

# 开启 rerere
git config --global rerere.enabled true

# 当解决完一次冲突后,Git 记录解决方案
# 下次遇到相同冲突时,自动应用记录的解

# 查看记录
git rerere status
git rerere diff

💡 典型场景:长期 feature 分支反复 rebase 到 main 时,rerere 会自动解决相同的冲突,大幅减少重复劳动。


Git LFS(大文件存储)

# 安装 Git LFS
git lfs install

# 追踪大文件
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "data/*.csv"

# 查看追踪模式
git lfs track

# .gitattributes 会被自动更新
# *.psd filter=lfs diff=lfs merge=lfs -text

# 之后像普通文件一样使用即可
git add file.psd
git commit -m "Add design file"
git push

⚡ Git LFS 将大文件的实际内容存储在单独的服务器上,仓库中只保存指针,大幅缩减 clone 时间。