Git 完全指南:入门到精通

Git 完全指南:入门到精通

📖 本文系统梳理 Git 所有核心知识点,从基础概念到高级技巧,配备大量实战案例,帮助你真正掌握 Git 的精髓。


目录

  1. [Git 基础概念](#Git 基础概念)
  2. 安装与初始配置
  3. 仓库操作
  4. 文件状态与暂存区
  5. 提交管理
  6. 分支管理
  7. 合并与变基
  8. 远程仓库
  9. 撤销与回退
  10. 标签管理
  11. 储藏(Stash)
  12. 高级操作
  13. [Git 工作流](#Git 工作流)
  14. 常见问题与解决方案
  15. 命令速查表

1. Git 基础概念

1.1 什么是 Git?

Git 是一个分布式版本控制系统,由 Linus Torvalds 于 2005 年创建。与集中式版本控制(如 SVN)不同,每个开发者本地都有完整的仓库副本。

复制代码
集中式(SVN):                分布式(Git):
    服务器                         服务器
      │                           ↑↓  ↑↓
   ┌──┴──┐                    开发者A  开发者B
开发者A  开发者B              (完整仓库)(完整仓库)

1.2 三个工作区域

复制代码
┌─────────────────────────────────────────────────────┐
│                                                     │
│  工作区          暂存区(Index)      本地仓库        │
│ Working Dir  →  Staging Area    →  Repository      │
│                                                     │
│  git add ──────────→                               │
│                    git commit ──────────→           │
│  git checkout ←─────────────────────────           │
│                                                     │
└─────────────────────────────────────────────────────┘

工作区:你实际编辑文件的地方
暂存区:准备提交的文件快照(.git/index)
本地仓库:已提交的历史记录(.git/objects)

1.3 文件的四种状态

复制代码
未跟踪(Untracked) ──git add──→ 已暂存(Staged)
                                    │
已跟踪(Tracked)  ←──git commit──────┘
    │
    ├── 未修改(Unmodified)
    │
    └── 已修改(Modified) ──git add──→ 已暂存(Staged)

1.4 核心对象模型

复制代码
Git 内部用四种对象存储数据:

blob    → 存储文件内容
tree    → 存储目录结构(包含blob和tree的引用)
commit  → 存储提交信息(包含tree、父commit、作者等)
tag     → 存储标签信息

每个对象用 SHA-1 哈希值唯一标识(40位十六进制)

1.5 HEAD 指针

复制代码
HEAD 是一个特殊指针,指向当前所在的分支(或提交)

正常状态:HEAD → main → commit C3
                         ↑
                    当前最新提交

分离HEAD状态:HEAD → commit C2(直接指向某个提交)
              此时不在任何分支上,新提交不属于任何分支

HEAD 的相对引用:
  HEAD^    → 父提交(上一个提交)
  HEAD^^   → 祖父提交
  HEAD~3   → 往前数3个提交
  HEAD~1   = HEAD^

2. 安装与初始配置

2.1 安装

bash 复制代码
# macOS
brew install git

# Ubuntu/Debian
sudo apt-get install git

# CentOS/RHEL
sudo yum install git

# Windows:下载 Git for Windows
# https://git-scm.com/download/win

2.2 全局配置(必做)

bash 复制代码
# 设置用户名和邮箱(每次提交都会用到)
git config --global user.name "张三"
git config --global user.email "zhangsan@example.com"

# 设置默认编辑器
git config --global core.editor vim        # 使用 vim
git config --global core.editor "code --wait"  # 使用 VS Code

# 设置默认分支名(Git 2.28+)
git config --global init.defaultBranch main

# 推荐配置
git config --global pull.rebase true       # pull 时默认 rebase
git config --global push.default current   # push 自动推到同名远程分支
git config --global fetch.prune true       # fetch 时自动清理失效远程分支
git config --global core.autocrlf input    # 统一换行符(macOS/Linux)

# 设置常用别名
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.cm "commit -m"
git config --global alias.lg "log --oneline --graph --all"

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

# 查看某项配置
git config user.name

2.3 配置文件层级

复制代码
系统级:/etc/gitconfig          (git config --system)
用户级:~/.gitconfig            (git config --global)
仓库级:.git/config             (git config --local)

优先级:仓库级 > 用户级 > 系统级

3. 仓库操作

3.1 初始化仓库

bash 复制代码
# 在当前目录初始化新仓库
git init

# 在指定目录初始化
git init my-project

# 初始化后目录结构
my-project/
├── .git/           ← Git 仓库数据(不要手动修改)
│   ├── HEAD        ← 当前分支指针
│   ├── config      ← 仓库配置
│   ├── objects/    ← 所有对象(blob/tree/commit/tag)
│   └── refs/       ← 分支和标签引用
└── (你的项目文件)

3.2 克隆仓库

bash 复制代码
# 克隆远程仓库
git clone https://github.com/user/repo.git

# 克隆到指定目录
git clone https://github.com/user/repo.git my-folder

# 克隆指定分支
git clone -b develop https://github.com/user/repo.git

# 浅克隆(只克隆最近N次提交,节省时间和空间)
git clone --depth 1 https://github.com/user/repo.git

# 克隆并包含子模块
git clone --recurse-submodules https://github.com/user/repo.git

4. 文件状态与暂存区

4.1 查看状态

bash 复制代码
# 查看工作区状态
git status

# 简洁模式
git status -s
# 输出说明:
# ?? → 未跟踪文件
# A  → 新文件已暂存
# M  → 已修改(左列=暂存区,右列=工作区)
# D  → 已删除

4.2 添加到暂存区

bash 复制代码
# 添加单个文件
git add README.md

# 添加多个文件
git add src/main.c src/utils.c

# 添加当前目录所有文件
git add .

# 添加所有已跟踪文件的修改(不包含新文件)
git add -u

# 交互式添加(可以选择性暂存文件的某些部分)
git add -p
# 按 y 暂存该块,n 跳过,s 拆分,e 手动编辑,q 退出

案例:只提交部分修改

bash 复制代码
# 场景:修改了 main.c,但只想提交其中一部分改动
git add -p main.c
# Git 会逐块显示修改,你可以选择哪些块进入暂存区
# 这样可以把一个文件的修改拆分成多个提交

4.3 查看差异

bash 复制代码
# 查看工作区 vs 暂存区的差异(未暂存的修改)
git diff

# 查看暂存区 vs 最新提交的差异(已暂存但未提交的修改)
git diff --staged
git diff --cached  # 同上

# 查看工作区 vs 某次提交的差异
git diff HEAD~2

# 查看两个分支的差异
git diff main feature/login

# 只显示有差异的文件名
git diff --name-only

# 显示文件名和修改统计
git diff --stat

4.4 .gitignore 文件

bash 复制代码
# .gitignore 示例
# 忽略所有 .log 文件
*.log

# 忽略 build 目录
build/

# 忽略特定文件
config/local.json

# 不忽略某个文件(即使上面的规则匹配)
!important.log

# 忽略根目录下的 TODO 文件(不忽略子目录中的)
/TODO

# 忽略 doc 目录下所有 .txt 文件(不递归)
doc/*.txt

# 忽略 doc 目录下所有 .pdf 文件(递归)
doc/**/*.pdf
bash 复制代码
# .gitignore 不生效的解决方法
# 原因:文件已经被 Git 跟踪,需要先从缓存中移除
git rm -r --cached .    # 清除所有缓存
git add .               # 重新添加(此时 .gitignore 生效)
git commit -m "fix: update .gitignore"

5. 提交管理

5.1 创建提交

bash 复制代码
# 基本提交
git commit -m "feat: 添加用户登录功能"

# 跳过暂存区,直接提交所有已跟踪文件的修改
git commit -am "fix: 修复登录验证bug"

# 打开编辑器写详细提交信息
git commit

# 修改最后一次提交(未push时使用)
git commit --amend -m "新的提交信息"

# 修改最后一次提交(不改信息,只改内容)
git add forgotten-file.txt
git commit --amend --no-edit

5.2 提交信息规范(Conventional Commits)

复制代码
格式:<类型>(<范围>): <描述>

类型:
  feat     → 新功能
  fix      → Bug修复
  docs     → 文档修改
  style    → 代码格式(不影响功能)
  refactor → 重构(不是新功能也不是修复)
  test     → 添加测试
  chore    → 构建过程或辅助工具的变动
  perf     → 性能优化
  ci       → CI配置修改

示例:
  feat(auth): 添加JWT令牌验证
  fix(payment): 修复重复扣款问题
  docs(api): 更新接口文档
  refactor(user): 重构用户模块代码结构

5.3 查看提交历史

bash 复制代码
# 基本日志
git log

# 单行显示
git log --oneline

# 图形化显示分支
git log --oneline --graph --all

# 显示最近N次提交
git log -5

# 显示某个文件的提交历史
git log --follow src/main.c

# 搜索提交信息
git log --grep="登录"

# 搜索代码变更(谁改了这行代码)
git log -S "function login"

# 显示某个时间范围的提交
git log --after="2024-01-01" --before="2024-12-31"

# 显示某个作者的提交
git log --author="张三"

# 显示每次提交的文件变更统计
git log --stat

# 显示每次提交的具体改动
git log -p

# 自定义格式
git log --pretty=format:"%h %an %ar %s"
# %h=短哈希 %an=作者名 %ar=相对时间 %s=提交信息

5.4 查看某次提交

bash 复制代码
# 查看最新提交的详情
git show

# 查看指定提交
git show abc1234

# 查看某次提交修改了哪些文件
git show --stat abc1234

# 查看某次提交中某个文件的内容
git show abc1234:src/main.c

6. 分支管理

6.1 分支基本操作

bash 复制代码
# 查看所有本地分支
git branch

# 查看所有分支(包含远程)
git branch -a

# 查看分支详情(含最新提交)
git branch -v

# 查看已合并到当前分支的分支
git branch --merged

# 查看未合并到当前分支的分支
git branch --no-merged

# 创建分支
git branch feature/login

# 切换分支
git checkout feature/login
git switch feature/login    # Git 2.23+ 推荐

# 创建并切换(一步到位)
git checkout -b feature/login
git switch -c feature/login  # Git 2.23+ 推荐

# 基于某个提交/标签创建分支
git checkout -b hotfix/v1.2 v1.2.0

# 重命名分支
git branch -m old-name new-name

# 删除已合并的分支
git branch -d feature/login

# 强制删除分支(未合并也删)
git branch -D feature/login

6.2 分支工作流案例

bash 复制代码
# 典型功能开发流程
git checkout main
git pull origin main              # 确保本地是最新的

git checkout -b feature/user-auth # 创建功能分支

# ... 开发代码 ...
git add .
git commit -m "feat: 添加用户认证模块"
git commit -m "test: 添加认证单元测试"

git push origin feature/user-auth  # 推送到远程

# 在 GitHub/GitLab 上创建 Pull Request / Merge Request
# 代码审查通过后合并到 main

# 合并后清理
git checkout main
git pull origin main
git branch -d feature/user-auth    # 删除本地分支
git push origin --delete feature/user-auth  # 删除远程分支

6.3 HEAD 与分支指针

bash 复制代码
# 查看 HEAD 指向
cat .git/HEAD
# 输出:ref: refs/heads/main

# 分离 HEAD(直接指向某个提交)
git checkout abc1234
# 警告:You are in 'detached HEAD' state

# 从分离 HEAD 状态创建新分支(保存工作)
git checkout -b new-branch

# 切回分支(放弃分离HEAD状态的提交)
git checkout main

7. 合并与变基

7.1 Merge(合并)

bash 复制代码
# 将 feature 分支合并到当前分支
git merge feature/login

# 强制生成合并提交(即使可以快进)
git merge --no-ff feature/login

# 快进合并(不生成合并提交,默认行为)
git merge --ff-only feature/login

# 合并但不提交(先查看合并结果)
git merge --no-commit feature/login

# 压缩合并(将feature的所有提交压缩为一个)
git merge --squash feature/login
git commit -m "feat: 合并用户登录功能"

Merge 的两种模式图解:

复制代码
快进合并(Fast-forward):
  main: A → B
  feature:     → C → D

  合并后:
  main: A → B → C → D
  (没有合并提交,历史是线性的)

非快进合并(--no-ff):
  main: A → B
  feature:     → C → D

  合并后:
  main: A → B ────────→ M(合并提交)
                ↗
  feature: C → D
  (保留了分支信息)

7.2 Rebase(变基)

bash 复制代码
# 将当前分支变基到 main
git rebase main

# 变基到远程 main
git rebase origin/main

# 交互式变基(整理最近4个提交)
git rebase -i HEAD~4

# 变基过程中的操作
git rebase --continue  # 解决冲突后继续
git rebase --skip      # 跳过当前提交
git rebase --abort     # 放弃变基,恢复原状

交互式变基(rebase -i)的操作指令:

复制代码
pick   → 保留该提交(默认)
reword → 保留提交,但修改提交信息
edit   → 保留提交,但暂停以便修改
squash → 将该提交合并到前一个提交(合并提交信息)
fixup  → 将该提交合并到前一个提交(丢弃提交信息)
drop   → 删除该提交

案例:整理提交历史

bash 复制代码
# 场景:开发过程中产生了很多零碎提交,推送前想整理
git log --oneline
# abc1234 fix typo
# def5678 fix another typo
# ghi9012 add login feature
# jkl3456 WIP: login

# 整理最近4个提交
git rebase -i HEAD~4

# 编辑器中修改为:
# pick jkl3456 WIP: login
# squash ghi9012 add login feature
# squash def5678 fix another typo
# squash abc1234 fix typo

# 保存后,Git 会将4个提交合并为1个
# 并让你编写新的提交信息

7.3 Merge vs Rebase 选择指南

复制代码
┌──────────────────┬──────────────────┬──────────────────┐
│  场景            │  推荐方式        │  原因            │
├──────────────────┼──────────────────┼──────────────────┤
│ 同步main到功能分支│ rebase           │ 保持线性历史     │
│ 功能分支合入main │ merge --no-ff    │ 保留分支信息     │
│ 已push的提交     │ 永远不要rebase   │ 会重写远程历史   │
│ 个人分支整理     │ rebase -i        │ 推送前清理历史   │
│ 紧急修复         │ merge            │ 快速且保留上下文 │
└──────────────────┴──────────────────┴──────────────────┘

黄金法则:
  ✅ 个人分支(未push)→ 随意使用 rebase
  ✅ 公共分支(已push)→ 只用 merge,禁止 rebase

7.4 解决合并冲突

bash 复制代码
# 冲突文件内容示例
<<<<<<< HEAD
const user = { name: 'Alice', age: 25 };
=======
const user = { name: 'Bob', age: 30 };
>>>>>>> feature/update-user

# <<<<<<< HEAD 到 ======= 是当前分支的内容
# ======= 到 >>>>>>> 是要合并进来的内容

# 解决步骤:
# 1. 打开冲突文件,手动编辑保留正确内容
# 2. 删除所有冲突标记(<<<, ===, >>>)
# 3. 暂存已解决的文件
git add src/user.js

# 4. 继续合并/变基
git merge --continue   # 或
git rebase --continue

# 查看冲突文件列表
git diff --name-only --diff-filter=U

# 使用工具解决冲突
git mergetool          # 使用配置的合并工具

8. 远程仓库

8.1 远程仓库管理

bash 复制代码
# 查看远程仓库
git remote -v

# 添加远程仓库
git remote add origin https://github.com/user/repo.git

# 添加多个远程仓库
git remote add upstream https://github.com/original/repo.git

# 修改远程仓库地址
git remote set-url origin https://github.com/user/new-repo.git

# 删除远程仓库
git remote remove upstream

# 重命名远程仓库
git remote rename origin upstream

8.2 fetch / pull / push

bash 复制代码
# ── fetch:只下载,不合并 ──────────────────────────
git fetch origin              # 获取所有远程分支的更新
git fetch origin main         # 只获取 main 分支的更新
git fetch --all               # 获取所有远程仓库的更新

# ── pull:下载并合并 ──────────────────────────────
git pull                      # = fetch + merge
git pull origin main          # 拉取远程 main 并合并
git pull --rebase             # = fetch + rebase(推荐)
git pull --rebase origin main

# ── push:推送到远程 ──────────────────────────────
git push origin main          # 推送 main 分支
git push origin HEAD          # 推送当前分支(推荐写法)
git push -u origin feature    # 首次推送并设置跟踪
git push --all                # 推送所有分支
git push --tags               # 推送所有标签

# 强制推送(rebase后使用,慎用!)
git push --force origin feature
git push --force-with-lease origin feature  # 更安全的强制推送

fetch vs pull 的区别:

复制代码
git fetch:
  远程仓库 → origin/main(远程跟踪分支)
  本地 main 分支不变,安全!

git pull:
  远程仓库 → origin/main → main(自动合并)
  相当于 git fetch + git merge origin/main

推荐习惯:
  重要分支先 fetch,检查后再手动 merge
  个人分支直接 pull --rebase

8.3 远程分支操作

bash 复制代码
# 查看远程分支
git branch -r

# 跟踪远程分支(创建本地分支并关联)
git checkout -b feature origin/feature
git checkout --track origin/feature  # 简写

# 设置已有分支的跟踪关系
git branch -u origin/main main
git branch --set-upstream-to=origin/main main

# 删除远程分支
git push origin --delete feature/old-branch
git push origin :feature/old-branch  # 等价写法

# 清理本地失效的远程分支引用
git fetch --prune
git remote prune origin

8.4 SSH 配置

bash 复制代码
# 生成 SSH 密钥
ssh-keygen -t ed25519 -C "your_email@example.com"

# 查看公钥(复制到 GitHub/GitLab)
cat ~/.ssh/id_ed25519.pub

# 测试 SSH 连接
ssh -T git@github.com

# 使用 SSH 克隆
git clone git@github.com:user/repo.git

# 切换已有仓库的协议(HTTPS → SSH)
git remote set-url origin git@github.com:user/repo.git

9. 撤销与回退

9.1 撤销工作区修改

bash 复制代码
# 丢弃工作区中某个文件的修改(恢复到暂存区状态)
git checkout -- src/main.c
git restore src/main.c          # Git 2.23+ 推荐

# 丢弃工作区所有修改
git checkout -- .
git restore .

# 删除未跟踪的文件(预览)
git clean -nd

# 删除未跟踪的文件(执行)
git clean -fd

# 删除未跟踪的文件和目录(包含 .gitignore 忽略的文件)
git clean -fdx

9.2 撤销暂存区(取消 add)

bash 复制代码
# 取消暂存某个文件(保留工作区修改)
git reset HEAD src/main.c
git restore --staged src/main.c  # Git 2.23+ 推荐

# 取消暂存所有文件
git reset HEAD .
git restore --staged .

9.3 reset(重置提交)

bash 复制代码
# 三种模式对比
git reset --soft HEAD~1   # 撤销提交,保留暂存区和工作区
git reset HEAD~1          # 撤销提交,清空暂存区,保留工作区(默认)
git reset --hard HEAD~1   # 撤销提交,清空暂存区和工作区(危险!)

# 回退到指定提交
git reset --hard abc1234

# 将本地重置为与远程完全一致
git fetch origin
git reset --hard origin/main

# 后悔了?撤销 reset(ORIG_HEAD 保存了 reset 前的状态)
git reset --hard ORIG_HEAD

reset 三种模式图解:

复制代码
初始状态:
  仓库: A → B → C(HEAD)
  暂存区: C的内容
  工作区: C的内容(可能有修改)

git reset --soft HEAD~1:
  仓库: A → B(HEAD)
  暂存区: C的内容(保留!)
  工作区: 不变
  → 适合:重新提交(修改提交信息或合并提交)

git reset HEAD~1(--mixed):
  仓库: A → B(HEAD)
  暂存区: B的内容(清空C的暂存)
  工作区: 不变(C的修改还在)
  → 适合:重新整理后提交

git reset --hard HEAD~1:
  仓库: A → B(HEAD)
  暂存区: B的内容
  工作区: B的内容(C的所有修改丢失!)
  → 适合:彻底放弃某次提交

9.4 revert(安全撤销)

bash 复制代码
# 创建一个新提交来撤销指定提交(安全,不改写历史)
git revert abc1234

# 撤销最近一次提交
git revert HEAD

# 撤销多个提交(不自动提交,手动提交)
git revert --no-commit HEAD~3..HEAD
git commit -m "revert: 撤销最近3次提交"

# 撤销合并提交(需要指定保留哪个父提交)
git revert -m 1 abc1234  # -m 1 表示保留第一个父提交(main分支)

reset vs revert 选择:

复制代码
git reset:
  ✅ 本地未push的提交
  ✅ 个人分支
  ❌ 已push到公共分支(会导致历史不一致)

git revert:
  ✅ 已push到公共分支
  ✅ 需要保留完整历史记录
  ✅ 团队协作场景

9.5 reflog(后悔药)

bash 复制代码
# 查看所有操作记录(包括已删除的提交)
git reflog

# 输出示例:
# abc1234 HEAD@{0}: commit: feat: 添加登录
# def5678 HEAD@{1}: reset: moving to HEAD~1
# ghi9012 HEAD@{2}: commit: fix: 修复bug

# 恢复到某个状态
git reset --hard HEAD@{2}

# 恢复误删的分支
git checkout -b recovered-branch abc1234

10. 标签管理

10.1 创建标签

bash 复制代码
# 轻量标签(只是一个指向提交的指针)
git tag v1.0.0

# 附注标签(包含标签信息、作者、日期)
git tag -a v1.0.0 -m "Release version 1.0.0"

# 给历史提交打标签
git tag -a v0.9.0 abc1234 -m "Beta release"

# 查看所有标签
git tag

# 查看标签详情
git show v1.0.0

10.2 推送和删除标签

bash 复制代码
# 推送单个标签
git push origin v1.0.0

# 推送所有标签
git push origin --tags

# 删除本地标签
git tag -d v1.0.0

# 删除远程标签
git push origin --delete v1.0.0
git push origin :refs/tags/v1.0.0  # 等价写法

# 检出标签(进入分离HEAD状态)
git checkout v1.0.0

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

11. 储藏(Stash)

11.1 基本操作

bash 复制代码
# 储藏当前工作区修改
git stash

# 储藏并添加描述
git stash push -m "WIP: 用户登录功能"

# 储藏包含未跟踪文件
git stash push -u -m "包含新文件的储藏"

# 查看所有储藏
git stash list
# 输出:
# stash@{0}: WIP on feature: abc1234 WIP: 用户登录功能
# stash@{1}: WIP on main: def5678 fix: 修复bug

# 恢复最新储藏(并从列表删除)
git stash pop

# 恢复指定储藏(不删除)
git stash apply stash@{1}

# 删除指定储藏
git stash drop stash@{0}

# 清空所有储藏
git stash clear

11.2 实战案例

bash 复制代码
# 场景:正在开发功能,突然需要切换分支修复紧急bug

# 1. 储藏当前工作
git stash push -m "WIP: 用户登录功能开发中"

# 2. 切换到 main 分支修复 bug
git checkout main
git checkout -b hotfix/payment-bug
# ... 修复 bug ...
git commit -m "fix: 修复支付重复扣款问题"
git push origin hotfix/payment-bug

# 3. 回到功能分支,恢复工作
git checkout feature/user-login
git stash pop

# 如果恢复时有冲突,解决后手动删除 stash
git stash drop

12. 高级操作

12.1 Cherry-pick(精选提交)

bash 复制代码
# 将某个提交应用到当前分支
git cherry-pick abc1234

# 应用多个提交
git cherry-pick abc1234 def5678

# 应用一个范围的提交(不含起点)
git cherry-pick abc1234..def5678

# 应用但不自动提交
git cherry-pick --no-commit abc1234

# 冲突后继续
git cherry-pick --continue

# 放弃 cherry-pick
git cherry-pick --abort

案例:从其他分支摘取特定修复

bash 复制代码
# 场景:同事在 develop 分支修复了一个 bug(提交 abc1234)
# 你需要把这个修复应用到 release 分支,但不想合并整个 develop

git checkout release/v1.2
git cherry-pick abc1234
# 只把那一个 bugfix 提交应用到 release 分支

12.2 Bisect(二分查找 Bug)

bash 复制代码
# 场景:某个功能在某次提交后出现了 bug,需要找到是哪次提交引入的

# 开始二分查找
git bisect start

# 标记当前版本是坏的
git bisect bad

# 标记某个已知正常的版本
git bisect good v1.0.0

# Git 会自动切换到中间的提交,你测试后标记
git bisect good   # 这个版本正常
git bisect bad    # 这个版本有问题

# Git 会不断缩小范围,最终找到引入 bug 的提交
# 找到后重置
git bisect reset

# 自动化二分查找(用脚本判断好坏)
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
git bisect run ./test.sh  # 脚本返回0=good,非0=bad

12.3 Worktree(多工作区)

bash 复制代码
# 场景:在 feature 分支开发时,需要同时查看/修改 main 分支

# 创建新工作区(检出 hotfix 分支)
git worktree add ../project-hotfix hotfix/login-bug

# 创建新工作区并新建分支
git worktree add -b emergency-fix ../project-fix main

# 查看所有工作区
git worktree list

# 删除工作区
git worktree remove ../project-hotfix

# 清理失效的工作区引用
git worktree prune

12.4 Submodule(子模块)

bash 复制代码
# 添加子模块
git submodule add https://github.com/user/lib.git libs/mylib

# 克隆含子模块的仓库
git clone --recurse-submodules https://github.com/user/repo.git

# 初始化子模块(克隆后忘记加 --recurse-submodules)
git submodule init
git submodule update

# 更新所有子模块到最新
git submodule update --remote

# 删除子模块
git submodule deinit libs/mylib
git rm libs/mylib
rm -rf .git/modules/libs/mylib

12.5 Git Hooks(钩子)

bash 复制代码
# 钩子存放在 .git/hooks/ 目录
# 常用钩子:
# pre-commit    → 提交前执行(代码检查、格式化)
# commit-msg    → 验证提交信息格式
# pre-push      → 推送前执行(运行测试)
# post-merge    → 合并后执行(安装依赖)

# 示例:pre-commit 钩子(代码格式检查)
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
# 运行代码格式检查
npm run lint
if [ $? -ne 0 ]; then
    echo "❌ 代码格式检查失败,请修复后再提交"
    exit 1
fi
echo "✅ 代码格式检查通过"
EOF
chmod +x .git/hooks/pre-commit

# 示例:commit-msg 钩子(验证提交信息格式)
cat > .git/hooks/commit-msg << 'EOF'
#!/bin/sh
commit_msg=$(cat "$1")
pattern="^(feat|fix|docs|style|refactor|test|chore|perf|ci)(\(.+\))?: .{1,72}$"
if ! echo "$commit_msg" | grep -qE "$pattern"; then
    echo "❌ 提交信息格式错误!"
    echo "正确格式:feat(scope): 描述"
    exit 1
fi
EOF
chmod +x .git/hooks/commit-msg

12.6 Blame(追责)

bash 复制代码
# 查看文件每一行的最后修改者
git blame src/main.c

# 只查看某几行
git blame -L 10,20 src/main.c

# 忽略空白字符变化
git blame -w src/main.c

# 显示原始提交(追踪代码移动)
git blame -C src/main.c

12.7 Archive(打包)

bash 复制代码
# 将当前版本打包为 zip
git archive --format=zip HEAD > release.zip

# 打包指定标签
git archive --format=tar.gz v1.0.0 > v1.0.0.tar.gz

# 只打包某个目录
git archive HEAD src/ | tar -x -C /tmp/

13. Git 工作流

13.1 GitFlow 工作流

复制代码
适合:有明确版本发布周期的项目

分支结构:
  main      → 生产环境,只接受 hotfix 和 release 合并
  develop   → 开发主干,功能分支的集成点
  feature/* → 功能开发分支(从 develop 创建)
  release/* → 发布准备分支(从 develop 创建)
  hotfix/*  → 紧急修复分支(从 main 创建)

流程:
  1. 从 develop 创建 feature 分支开发
  2. 完成后合并回 develop
  3. 从 develop 创建 release 分支
  4. release 分支只做 bug 修复和文档
  5. release 合并到 main(打标签)和 develop
  6. 线上 bug 从 main 创建 hotfix 分支
  7. hotfix 合并到 main 和 develop
bash 复制代码
# GitFlow 实践
# 开始新功能
git checkout develop
git checkout -b feature/user-auth

# 完成功能
git checkout develop
git merge --no-ff feature/user-auth
git branch -d feature/user-auth

# 准备发布
git checkout -b release/v1.2.0 develop
# 修复发布相关问题...
git checkout main
git merge --no-ff release/v1.2.0
git tag -a v1.2.0 -m "Release v1.2.0"
git checkout develop
git merge --no-ff release/v1.2.0
git branch -d release/v1.2.0

# 紧急修复
git checkout -b hotfix/v1.2.1 main
# 修复...
git checkout main
git merge --no-ff hotfix/v1.2.1
git tag -a v1.2.1 -m "Hotfix v1.2.1"
git checkout develop
git merge --no-ff hotfix/v1.2.1
git branch -d hotfix/v1.2.1

13.2 GitHub Flow(简化工作流)

复制代码
适合:持续部署、小团队、Web 应用

规则:
  1. main 分支始终可部署
  2. 从 main 创建描述性分支名
  3. 频繁推送到远程同名分支
  4. 随时创建 Pull Request 讨论
  5. 审查通过后合并到 main
  6. 合并后立即部署
bash 复制代码
# GitHub Flow 实践
git checkout main
git pull origin main
git checkout -b feature/add-payment

# 开发...
git add .
git commit -m "feat: 添加支付功能"
git push origin feature/add-payment

# 在 GitHub 创建 Pull Request
# 团队审查代码
# 审查通过后合并到 main
# 自动触发 CI/CD 部署

# 清理
git checkout main
git pull origin main
git branch -d feature/add-payment

13.3 Trunk-Based Development

复制代码
适合:高频发布、大型团队、DevOps 成熟的团队

规则:
  1. 所有开发者频繁合并到 main(每天至少一次)
  2. 功能分支生命周期不超过 2 天
  3. 使用 Feature Flag 控制未完成功能
  4. 强大的 CI/CD 保障质量

14. 常见问题与解决方案

14.1 误操作恢复

bash 复制代码
# 问题1:误删分支,如何恢复?
git reflog                          # 找到分支最后的提交哈希
git checkout -b recovered abc1234   # 基于该提交重建分支

# 问题2:reset --hard 后想恢复
git reflog                          # 找到 reset 前的状态
git reset --hard HEAD@{1}           # 恢复到 reset 前

# 问题3:误提交了敏感信息(密码/密钥)
# 方法1:修改最后一次提交(未push)
git commit --amend                  # 删除敏感信息后重新提交

# 方法2:已push,需要重写历史(危险!需通知团队)
git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch config/secrets.json' \
  --prune-empty --tag-name-filter cat -- --all
git push --force --all

# 更好的方法:使用 git-filter-repo 工具
pip install git-filter-repo
git filter-repo --path config/secrets.json --invert-paths

14.2 常见错误处理

bash 复制代码
# 错误:push 被拒绝(远程有新提交)
# ! [rejected] main -> main (fetch first)
git pull --rebase origin main       # 先拉取再推送
git push origin main

# 错误:合并冲突无法解决,想放弃
git merge --abort                   # 放弃合并
git rebase --abort                  # 放弃变基

# 错误:.gitignore 不生效
git rm -r --cached .                # 清除缓存
git add .
git commit -m "fix: update .gitignore"

# 错误:提交到了错误的分支
# 场景:本应提交到 feature,却提交到了 main
git log --oneline -3                # 找到误提交的哈希
git checkout feature
git cherry-pick abc1234             # 把提交移到正确分支
git checkout main
git reset --hard HEAD~1             # 从 main 删除该提交

# 错误:大文件提交导致 push 失败
# 使用 Git LFS(Large File Storage)
git lfs install
git lfs track "*.psd"
git lfs track "*.zip"
git add .gitattributes
git add large-file.psd
git commit -m "add large file via LFS"

14.3 性能优化

bash 复制代码
# 清理仓库(删除无用对象)
git gc

# 深度清理
git gc --aggressive --prune=now

# 查看仓库大小
git count-objects -vH

# 找出大文件
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  sort -k3 -n -r | head -20

15. 命令速查表

基础操作

bash 复制代码
git init                    # 初始化仓库
git clone <url>             # 克隆仓库
git status                  # 查看状态
git add <file>              # 暂存文件
git add .                   # 暂存所有
git commit -m "msg"         # 提交
git log --oneline --graph   # 查看历史
git diff                    # 查看未暂存的修改
git diff --staged           # 查看已暂存的修改

分支操作

bash 复制代码
git branch                  # 查看分支
git branch <name>           # 创建分支
git checkout <name>         # 切换分支
git checkout -b <name>      # 创建并切换
git merge <branch>          # 合并分支
git branch -d <name>        # 删除分支
git branch -D <name>        # 强制删除

远程操作

bash 复制代码
git remote -v               # 查看远程
git remote add origin <url> # 添加远程
git fetch origin            # 获取远程更新
git pull origin main        # 拉取并合并
git push origin main        # 推送
git push -u origin HEAD     # 首次推送并跟踪
git push --force-with-lease # 安全强制推送

撤销操作

bash 复制代码
git restore <file>          # 丢弃工作区修改
git restore --staged <file> # 取消暂存
git reset --soft HEAD~1     # 撤销提交(保留暂存)
git reset HEAD~1            # 撤销提交(保留工作区)
git reset --hard HEAD~1     # 彻底撤销提交
git revert HEAD             # 安全撤销(创建新提交)
git reflog                  # 查看操作历史(后悔药)

高级操作

bash 复制代码
git stash                   # 储藏修改
git stash pop               # 恢复储藏
git cherry-pick <hash>      # 精选提交
git rebase main             # 变基
git rebase -i HEAD~3        # 交互式变基
git bisect start/good/bad   # 二分查找bug
git blame <file>            # 查看行修改者
git tag -a v1.0 -m "msg"    # 创建标签
git worktree add <path>     # 添加工作区

实用别名配置

bash 复制代码
git config --global alias.st "status"
git config --global alias.co "checkout"
git config --global alias.br "branch"
git config --global alias.cm "commit -m"
git config --global alias.lg "log --oneline --graph --all --decorate"
git config --global alias.undo "reset HEAD~1 --mixed"
git config --global alias.unstage "restore --staged"
git config --global alias.last "log -1 HEAD --stat"
git config --global alias.aliases "config --get-regexp alias"

附录:Git 学习路线

复制代码
入门阶段:
  ✅ git init / clone
  ✅ git add / commit / push / pull
  ✅ git status / log / diff
  ✅ 基本分支操作(branch / checkout / merge)

进阶阶段:
  ✅ rebase(变基)
  ✅ reset / revert(撤销)
  ✅ stash(储藏)
  ✅ cherry-pick(精选)
  ✅ 远程仓库管理
  ✅ 解决合并冲突

高级阶段:
  ✅ 交互式 rebase(rebase -i)
  ✅ bisect(二分查找)
  ✅ worktree(多工作区)
  ✅ submodule(子模块)
  ✅ hooks(钩子)
  ✅ reflog(操作历史)
  ✅ Git 工作流设计

精通阶段:
  ✅ Git 内部原理(对象模型)
  ✅ 自定义 Git 命令
  ✅ Git LFS(大文件管理)
  ✅ 历史重写(filter-branch / filter-repo)
  ✅ 团队 Git 规范制定

💡 核心原则总结

  1. 频繁提交,小步前进 --- 每个提交只做一件事
  2. 个人分支随意折腾,公共分支只做加法 --- 已push的不要rebase
  3. 用 rebase 同步,用 merge 合并 --- 保持历史整洁
  4. 危险操作前先备份 --- git checkout -b backup_$(date +%Y%m%d)
  5. reflog 是最后的救命稻草 --- 几乎所有误操作都能恢复