用途
Git 是现代分布式版本控制的基础工具,具备健壮的分支模型、交互式历史重写、稀疏检出(Sparse Checkout)以及面向单体仓库(monorepo)的 Scalar 支持。本技能提供专家级 Git 版本控制辅助,覆盖分支管理、交互式 rebase、cherry-pick、子模块、工作树(worktree)与冲突解决等场景。
何时使用
- 分支与协作:管理功能隔离、拉取请求(pull request)、主干开发(trunk-based development)以及分布式团队间的代码评审。
- 历史重写与清理:在合并到主线分支前,通过
git rebase -i线性化提交图、压缩 WIP 提交并编辑提交信息。 - 自动化缺陷定位:使用
git bisect通过二分查找在数千个修订中定位引入回归的提交。 - 并发工作区上下文:使用
git worktree处理紧急热修复,无需 stash 或切换分支上下文。 - 大型仓库扩展:使用 Git 稀疏检出、Scalar 和浅克隆优化大型单体仓库的性能。
快速开始
# 克隆仓库并创建新的功能分支
git clone https://github.com/myorg/project.git
cd project
# 使用现代命令创建并切换到新分支
git switch -c feature/auth-flow
# 暂存、提交并推送并设置上游跟踪
git add .
git commit -m "feat(auth): implement oauth2 authorization code flow"
git push -u origin feature/auth-flow
核心概念
三树架构(工作树、索引、HEAD)
Git 通过三种不同状态管理文件,而不仅仅是跟踪修订之间的变更:
# 1. 工作树:编辑器中已修改但未提交的文件
echo "export const API_URL = 'https://api.v2.internal';" >> config.ts
# 2. 索引(暂存区):为下一次提交准备的内容
git add config.ts
# 3. HEAD:当前分支上最后一次提交的快照
git diff --staged # 查看暂存区与 HEAD 之间的差异
git commit -m "chore(config): update internal API endpoint"
有向无环图(DAG)与不可变对象
Git 中的每个提交都是一个不可变节点,指向父哈希和由 blob 快照组成的根树。
# 查看任意提交哈希或 HEAD 背后的原始对象
git cat-file -p HEAD
# 输出:
# tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
# parent 8f3d1e1c3a628a58a98b47cf7d12a9e32049e712
# author Alice <alice@example.com> 1727420400 +0000
# committer Alice <alice@example.com> 1727420400 +0000
#
# feat(auth): implement oauth2 authorization code flow
现代分支导航(git switch 与 git restore)
现代 Git 将重载的 git checkout 命令拆分为专用操作:
# 分支切换(替代 git checkout <branch>)
git switch main
git switch -c feature/payments # 创建并切换
# 文件恢复(替代 git checkout -- <file>)
git restore src/app.ts # 丢弃工作目录中未提交的更改
git restore --staged src/app.ts # 将文件从索引取消暂存回工作树
Rebase 与 Merge 策略
- Merge(
git merge <branch>):通过双父提交保留完整历史上下文。适合集成长期存在的发布分支。 - Rebase(
git rebase <upstream>):将主题提交线性重放到上游 HEAD 之上,避免杂乱的“铁轨”图。适合在 PR 评审前清理功能分支。
# 将功能分支干净地 rebase 到最新的 main
git switch feature/auth-flow
git fetch origin main
git rebase origin/main
常见模式
使用 Git Worktrees 进行多分支开发
问题:需要 stash 未提交的工作来切换分支以修复紧急生产缺陷。
解决方案:同时将多个分支检出到不同目录:
# 为热修复分支创建并行工作树文件夹
git worktree add ../hotfix-branch main
# 在单独目录中处理热修复,不影响当前工作区
cd ../hotfix-branch
git commit -am "fix: patch critical auth vulnerability"
git push origin hotfix-branch
# 完成后清理工作树
cd ../project
git worktree remove ../hotfix-branch
交互式 Rebase 打造干净的拉取请求历史
问题:功能分支包含数十个杂乱的“WIP”、“fix typo”或“checkpoint”提交。
解决方案:在打开拉取请求前交互式压缩并清理历史。
# 交互式 rebase 最近 4 个提交
git rebase -i HEAD~4
# 在交互式编辑器中:
# pick 8f3d1e1 feat(auth): add token generation
# squash 4b825dc fix typo in token payload
# squash a1c94e2 add missing import
# reword 91f7a02 feat(auth): add refresh token endpoint
使用 Git Bisect 自动定位回归
问题:缺陷在 500 个提交中的某处引入,但确切提交未知。
解决方案:使用自动化测试执行脚本运行二分查找。
# 开始 bisect 会话
git bisect start
git bisect bad HEAD # 当前 HEAD 已损坏
git bisect good v2.1.0 # v2.1.0 已知正常
# 在每个 bisect 步骤自动运行测试脚本
git bisect run npm test -- --runInBand
# Git 在第一个破坏测试套件的提交处终止
git bisect reset
最佳实践
推荐做法
- 使用
git switch和git restore:避免重载的git checkout,防止意外切换分支或覆盖文件。 - 使用
--force-with-lease推送:防止覆盖远程分支上同事的提交。 - 启用后台维护:运行
git maintenance start以优化 packfile、提交图和拉取性能。 - 使用 SSH / GPG 签名提交:在生产流水线中保证提交作者身份的真实性。
- 编写约定式提交(Conventional Commits):使用标准化前缀(feat:、fix:、chore:、refactor:)自动化语义化版本和变更日志。
避免做法
- 强制推送到受保护分支:绝不要在 main、master 或共享发布分支上重写历史。
- 提交机密或凭据:使用 .gitignore 和 git-secrets/trufflehog;一旦提交,凭据即使后来删除也会保留在 packfile blob 中。
- 直接使用巨大二进制文件:将大型模型权重或媒体存储在 Git LFS(Large File Storage)或 S3 中。
- 合并脏工作树:在拉取或 rebase 前 stash 或提交更改,避免意外文件冲突。
故障排除
| 错误 | 原因 | 解决方案 |
|---|---|---|
| fatal: refusing to merge unrelated histories | 从独立初始化且无共享根提交的仓库拉取。 | 运行 git pull origin main --allow-unrelated-histories。 |
| HEAD detached at ... | 检出了特定提交哈希而非分支名。 | 从当前位置创建分支:git switch -c recovery-branch。 |
| Merge conflict in file.txt | 并发提交在相反分支上修改了相同行。 | 解决冲突标记(<<<<<<<),暂存文件(git add),然后运行 git commit。 |
参考资料
- Git Documentation