Chapter 10

GitHub 协作与 Git 内部原理

深入 Git 对象模型——理解一切皆 SHA 指针的设计,以及 GitHub 高级协作实践

GitHub 协作最佳实践

PR 大小与描述

一个好的 Pull Request 的准则:

PR 描述模板

在仓库 .github/PULL_REQUEST_TEMPLATE.md 中设置 PR 模板:

## Why(为什么做这个改动)
关联 Issue: #123
解决了用户无法在移动端完成注册的问题。

## What(做了什么改动)
- 修复了手机号验证正则表达式
- 增加了对国际区号的支持
- 新增了 3 个单元测试用例

## How to test(如何测试)
- [ ] 访问 /register,用手机号注册
- [ ] 测试国际号码格式(+1-555-1234567)
- [ ] 确认错误信息显示正确

## Screenshots(截图,如有 UI 变更)

CODEOWNERS 文件

.github/CODEOWNERS 文件中定义代码所有者,当相关路径的文件被修改时,自动添加指定成员为 Reviewer:

# .github/CODEOWNERS
# 格式:路径模式  @用户名或@组

# 整个仓库的默认 owner
*                    @org/core-team

# 前端代码由前端团队负责
/src/frontend/       @org/frontend-team

# 支付相关代码需要特定成员审核
/src/payment/        @alice @bob

# 基础设施配置
/infra/              @org/devops-team
*.yml                @org/devops-team

GitHub 高级功能

Protected Branch(保护分支)

在 GitHub 仓库的 Settings → Branches 中可以为 main 等重要分支设置保护规则:

GitHub Actions 简介

GitHub Actions 是 GitHub 内置的 CI/CD 平台,与 Git Hooks 的核心区别:

维度Git HooksGitHub Actions
运行位置开发者本地机器GitHub 云端服务器
可被绕过可以(--no-verify)不可以(服务端运行)
触发条件Git 命令事件push、PR、定时等任意事件
典型用途本地快速检查完整 CI、自动部署
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: npm ci
      - run: npm test
      - run: npm run build

Git 内部原理——对象模型

理解 Git 的内部原理,是真正掌握 Git 的关键。Git 的底层其实是一个简单而优雅的内容寻址文件系统(Content-Addressable Filesystem)。

四种 Git 对象

Git 中的一切都存储为对象,存放在 .git/objects/ 目录中。每个对象由其内容的 SHA-1 哈希唯一标识。

对象之间的关系

tag v1.0.0

commit ──▶ parent commit ──▶ ...

tree (root/)
├──▶ blob (README.md)
├──▶ tree (src/)
│ ├──▶ blob (app.js)
│ └──▶ blob (utils.js)
└──▶ tree (tests/)
└──▶ blob (app.test.js)

亲手查看 Git 对象

# 查看某个文件对应的 blob 哈希
git hash-object README.md

# 查看 HEAD 指向的 commit 对象内容
git cat-file -p HEAD

# 输出示例:
# tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
# parent a3f2c1d8e5b9f2130459a781cd13f58cc6f88c9
# author 张三 <zhang@example.com> 1710000000 +0800
# committer 张三 <zhang@example.com> 1710000000 +0800
# feat: add profile page

# 查看 tree 对象的内容
git cat-file -p HEAD^{tree}
# 输出:100644 blob abc1234... README.md
#       040000 tree def5678... src

# 查看对象类型
git cat-file -t HEAD  # 输出:commit

# 在 .git/objects/ 中直接查看(二进制压缩,需要解压)
ls .git/objects/

引用(Refs)

分支和标签都是引用——存储在 .git/refs/ 目录下的文本文件,内容就是一个 40 字符的 commit 哈希。

# 查看 main 分支指向哪个 commit
cat .git/refs/heads/main

# 查看某个 tag 指向哪个对象
cat .git/refs/tags/v1.0.0

# 查看 HEAD 文件(指向当前分支)
cat .git/HEAD
# 输出:ref: refs/heads/main

# 创建分支本质上就是创建一个包含 commit 哈希的文件
echo a3f2c1d... > .git/refs/heads/new-branch

"Git 中一切都是 SHA 指针"的意义

理解这个设计哲学,可以解释很多 Git 的行为:

常见 Git 问题排查

问题 1:合并后发现 bug,如何回滚到合并前

# 找到 merge commit 的哈希
git log --oneline --merges

# 方案 A:revert merge commit(安全,适合公共分支)
# -m 1 表示保留第一个父(被合并进来的 main)
git revert -m 1 merge-commit-hash

# 方案 B:reset --hard 到合并前(适合个人分支)
git reset --hard commit-before-merge

问题 2:不小心提交了密钥,如何从历史中彻底删除

!

密钥一旦提交并推送,应立即撤销密钥!即使从历史中删除,也无法确保没有人缓存了那段历史。删除历史只是减少曝光风险,不能代替撤销密钥。

# 使用 git-filter-repo(推荐,比 filter-branch 快得多)
pip install git-filter-repo

# 从所有历史中删除含有密钥的文件
git filter-repo --path .env --invert-paths

# 或者替换文件中的敏感内容
git filter-repo --replace-text replacements.txt
# replacements.txt 格式:
# literal:my-secret-key==>REDACTED

# 处理完后强制推送所有分支(会改写历史!)
git push --force --all
git push --force --tags

# 通知所有协作者重新 clone 仓库

BFG Repo Cleaner 是另一个流行工具(bfg.jar),比 git filter-repo 更简单但功能略少。命令:java -jar bfg.jar --delete-files .env

问题 3:大文件误入仓库

将大型二进制文件(视频、数据集、编译产物)提交到 Git 仓库会导致仓库体积膨胀,克隆速度极慢。

# 使用 git-filter-repo 从历史中移除大文件
git filter-repo --strip-blobs-bigger-than 10M

# 或者使用 Git LFS(Large File Storage)管理大文件
# Git LFS 用指针文件替代实际的大文件,实际内容存储在 LFS 服务器

# 安装 Git LFS
git lfs install

# 追踪特定类型的大文件
git lfs track "*.psd"
git lfs track "*.mp4"
git lfs track "datasets/**"

# 必须将 .gitattributes 文件提交到仓库
git add .gitattributes
git commit -m "chore: configure Git LFS for large files"

# 之后 add/commit/push 大文件,它们会自动走 LFS 通道

问题 4:仓库太大,克隆太慢

# 浅克隆(只拉取最近 N 次提交)
git clone --depth 1 https://github.com/user/large-repo.git

# 只克隆指定分支
git clone --single-branch --branch main URL

# 部分克隆(Git 2.19+,只下载需要的对象)
git clone --filter=blob:none URL  # 不下载 blob,按需加载

Git 维护命令

随着仓库的增长,定期维护可以保持仓库性能和健康状态。

# 运行完整的仓库健康检查和垃圾回收
git gc

# 更激进的垃圾回收(清理所有不可达对象)
git gc --aggressive --prune=now

# 检查仓库对象完整性(检查哈希)
git fsck

# 只检查可达对象(更快)
git fsck --reachable

# 统计仓库大小和对象数量
git count-objects -vH
# 输出:count: 1234(松散对象数量)
#       size: 2.34 MiB(松散对象总大小)
#       in-pack: 45678(打包对象数量)
#       size-pack: 123.45 MiB(pack 文件总大小)

# 查找仓库中最大的对象(排查大文件)
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  grep '^blob' | sort --numeric-sort -k3 -r | head -20

# 打包松散对象(提升读取性能)
git repack -ad

# 清理过期的 reflog 条目(加快 gc)
git reflog expire --expire=90.days --all

GitHub CLI(gh)与 Git 集成

GitHub CLI(gh)是在命令行操作 GitHub 的官方工具,与 Git 无缝配合:

# 安装(macOS)
brew install gh

# 认证登录
gh auth login

# 从 GitHub 创建仓库并推送当前代码
gh repo create my-project --public --push

# 从当前分支创建 Pull Request
gh pr create --title "feat: add login" --body "Implements #42"

# 查看当前仓库的 PR 列表
gh pr list

# 检出某个 PR(本地测试)
gh pr checkout 123

# 批准并合并一个 PR
gh pr review 123 --approve
gh pr merge 123 --squash --delete-branch

# 查看 CI 运行状态
gh run list
gh run view 12345678

# 克隆并自动配置上游
gh repo clone owner/repo

git config 高级配置

一些提升日常效率的全局配置:

# 设置 pull 默认使用 rebase(强烈推荐)
git config --global pull.rebase true

# 启用 rerere(记忆冲突解决方案)
git config --global rerere.enabled true

# 设置默认分支为 main
git config --global init.defaultBranch main

# 显示更友好的 diff(按单词高亮)
git config --global diff.colorMoved zebra

# 推送时自动设置上游(无需 -u 参数)
git config --global push.autoSetupRemote true

# 提交时自动修正 gpg 签名问题(如果使用签名提交)
git config --global commit.gpgSign false  # 或 true 开启签名

# 常用别名
git config --global alias.lg "log --oneline --graph --all --decorate"
git config --global alias.st "status -s"
git config --global alias.co "checkout"
git config --global alias.br "branch -vv"
git config --global alias.unstage "restore --staged"

# 查看所有配置
git config --global --list

学习之旅完成!你已经学习了从 Git 基础到内部原理的全部核心内容。Git 的本质是一个内容寻址的对象数据库(blob/tree/commit/tag),一切都是 SHA 指针。分支是轻量的可移动指针,worktree 支持并行多分支工作,rerere 减少重复解决冲突的劳动,scalar 为大型仓库提供性能优化,reflog 是任何误操作的最后保障。Git 是一门需要实践的技艺——从今天起,在每个项目中有意识地运用这些知识。