阶段零:Git 完全指南
Git 完全指南:从入门到原理精通
目录
- Git 是什么
- Git 的核心概念与工作原理
- 安装与配置
- 基础操作(clone, add, commit, push)
- 分支管理
- 远程仓库协作
- 撤销与回退
- 高级技巧
- Git 内部原理深度解析
- 常见场景实战
1. Git 是什么
Git 是一个分布式版本控制系统,由 Linux 之父 Linus Torvalds 于 2005 年创造。
核心价值:
- 记录文件的变更历史
- 支持多人协作开发
- 可以随时回溯到任意历史版本
与 SVN 的核心区别:
| 特性 | Git(分布式) | SVN(集中式) |
|---|---|---|
| 仓库位置 | 每个开发者都有完整副本 | 只有中央服务器有完整副本 |
| 提交速度 | 极快(本地操作) | 慢(需要网络) |
| 离线工作 | 完全支持 | 基本不支持 |
| 分支成本 | 极低(瞬间创建) | 高(复制整个目录) |
2. Git 的核心概念与工作原理
2.1 三个区域
Git 将文件管理分为三个区域:
工作区 (Working Directory)
↓ git add
暂存区 (Staging Area / Index)
↓ git commit
本地仓库 (Local Repository)
↓ git push
远程仓库 (Remote Repository)
| 区域 | 说明 | 位置 |
|---|---|---|
| 工作区 | 你正在编辑的文件 | 项目目录 |
| 暂存区 | 准备提交的文件清单 | .git/index |
| 本地仓库 | 已提交的历史版本 | .git/objects |
| 远程仓库 | 托管在服务器上的仓库 | GitHub/GitLab 等 |
2.2 文件状态
未跟踪 (Untracked) → 新文件,从未被 Git 管理
已跟踪 (Tracked) → 被 Git 管理的文件
├── 未修改 (Unmodified) → 与上次提交相同
├── 已修改 (Modified) → 内容改变,未暂存
└── 已暂存 (Staged) → 已加入暂存区,待提交
2.3 Git 的数据模型本质
Git 本质上是一个内容寻址的文件系统,核心是一个键值存储:
- Key:SHA-1 哈希值(40 位十六进制)
- Value:文件内容或元数据
# 任何内容都会被计算 SHA-1 哈希
echo "hello" | git hash-object --stdin
# 输出: ce013625030ba8dba906f756967f9e9ca394464a
四种对象类型:
| 对象类型 | 作用 | 存储内容 |
|---|---|---|
| Blob | 存储文件内容 | 文件的数据内容 |
| Tree | 存储目录结构 | 文件名 → Blob/Tree 的映射 |
| Commit | 存储快照信息 | Tree 哈希 + 父 Commit + 作者 + 消息 |
| Tag | 标记特定版本 | 指向 Commit 的引用 |
对象关系图:
Commit (ca82a6d)
├── Tree (0155eb)
│ ├── blob (f79e4a) README.md
│ ├── blob (8a7b3c) main.py
│ └── Tree (3c4e9d) src/
│ └── blob (b2d1f8) utils.py
├── Parent: a1e2b3c (上一个 Commit)
├── Author: 五味子
└── Message: "Initial commit"
3. 安装与配置
3.1 安装 Git
| 操作系统 | 安装方式 |
|---|---|
| Windows | 下载 git-scm.com 安装包 |
| macOS | brew install git |
| Linux (Ubuntu) | sudo apt install git |
| Linux (CentOS) | sudo yum install git |
验证安装:
git --version
# git version 2.40.0
3.2 首次配置
# 设置用户名和邮箱(必填,每次提交会记录)
git config --global user.name "五味子"
git config --global user.email "wuweizi@example.com"
# 设置默认分支名
git config --global init.defaultBranch main
# 设置别名(提高效率)
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.lg "log --oneline --graph --all"
# 查看所有配置
git config --list
配置文件层级:
--local:项目级别(.git/config)—— 优先级最高--global:用户级别(~/.gitconfig)—— 最常用--system:系统级别(/etc/gitconfig)—— 优先级最低
4. 基础操作(clone, add, commit, push)
4.1 初始化或克隆仓库
# 方式一:创建全新仓库
mkdir my-project
cd my-project
git init
# 此时 .git 目录被创建
# 方式二:克隆远程仓库
git clone https://github.com/username/repo.git
git clone git@github.com:username/repo.git # SSH 方式
git clone --depth 1 https://github.com/username/repo.git # 浅克隆(只获取最新版本)
git init 做了什么?
.git/
├── HEAD # 当前指向的分支
├── config # 项目配置
├── description # 仓库描述
├── hooks/ # 钩子脚本
├── info/ # 排除文件
├── objects/ # 所有对象(blob/tree/commit)
├── refs/ # 引用(分支、标签)
│ ├── heads/ # 本地分支
│ └── tags/ # 标签
└── index # 暂存区文件
4.2 查看状态
# 查看工作区和暂存区状态
git status
# 简洁模式
git status -s
# ?? newfile.txt (未跟踪)
# M modified.txt (已修改未暂存)
# M staged.txt (已暂存)
4.3 git add - 将文件加入暂存区
# 添加单个文件
git add README.md
# 添加所有变更
git add .
git add --all
git add -A
# 添加所有 .py 文件
git add *.py
# 交互式添加(可选择性暂存)
git add -i
# 添加文件的部分修改(不添加整个文件)
git add -p
git add 的原理:
- 计算文件内容的 SHA-1 哈希
- 将文件内容压缩后存入
.git/objects作为 Blob 对象 - 将文件路径和 Blob 哈希记录到
.git/index(暂存区)
# 手动查看 Git 对象
git hash-object README.md # 计算 SHA-1
find .git/objects -type f # 查看所有对象
git cat-file -p <hash> # 查看对象内容
4.4 git commit - 提交到本地仓库
# 基本提交
git commit -m "添加了 README 文件"
# 提交并打开编辑器写多行消息
git commit
# 跳过暂存区,直接提交所有已修改文件
git commit -a -m "直接提交所有修改"
# 修改上一次提交(不产生新 commit)
git commit --amend -m "修正的提交消息"
# 添加遗漏文件到上一次提交
git add forgotten.txt
git commit --amend --no-edit
git commit 的原理:
- 读取
.git/index中的暂存区信息 - 创建一个 Tree 对象(目录结构快照)
- 创建 Commit 对象:
- Tree 哈希
- 父 Commit 哈希(HEAD 指向的)
- 作者信息
- 时间戳
- 提交消息
- 将当前分支的引用(如
refs/heads/main)指向新 Commit
# 查看 Commit 对象内容
git cat-file -p HEAD
# tree 0155eb422c163f2c2d6f0e9d6e8f9a7c5b3e1a2d
# parent a1e2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0
# author 五味子 <wuweizi@example.com> 1712900000 +0800
# committer 五味子 <wuweizi@example.com> 1712900000 +0800
#
# Initial commit
4.5 git push - 推送到远程仓库
# 添加远程仓库
git remote add origin https://github.com/username/repo.git
# 查看远程仓库
git remote -v
# origin https://github.com/username/repo.git (fetch)
# origin https://github.com/username/repo.git (push)
# 推送当前分支到远程
git push -u origin main # -u 设置上游跟踪
# 之后可以直接用
git push
# 推送所有分支
git push --all
# 推送标签
git push --tags
# 强制推送(危险!覆盖远程历史)
git push --force
git push -f
git push 的原理:
- 与远程仓库建立连接(HTTPS/SSH)
- 交换引用信息(双方有哪些 Commit)
- 传输本地有但远程没有的 Commit 对象
- 更新远程分支指向最新的 Commit
- 快进合并(fast-forward)或拒绝(非快进)
5. 分支管理
5.1 分支的本质
分支本质上只是一个指向 Commit 的指针(文件),存储在 .git/refs/heads/ 中。
# 查看分支引用文件
cat .git/refs/heads/main
# 输出: ca82a6dff817ec66f44342007202690a93763949
创建分支只是创建一个新文件,成本极低。
5.2 基本分支操作
# 查看分支
git branch # 本地分支
git branch -r # 远程分支
git branch -a # 所有分支
git branch -v # 分支+最后一次提交
# 创建分支
git branch feature-login
# 切换分支
git checkout feature-login
git switch feature-login # 新版命令
# 创建并切换
git checkout -b feature-login
git switch -c feature-login
# 合并分支
git checkout main
git merge feature-login
# 删除分支
git branch -d feature-login # 已合并的分支
git branch -D feature-login # 强制删除未合并分支
5.3 合并的两种方式
快进合并(Fast-Forward):
main: A---B---C
↑
feature: C---D---E
合并后 main 直接指向 E,无新 Commit
三方合并(3-Way Merge):
main: A---B---C
\
feature: D---E
合并后创建新 Commit F
# 强制非快进合并(保留分支历史)
git merge --no-ff feature-login
# 压缩合并(将多个 Commit 合并为一个)
git merge --squash feature-login
5.4 解决冲突
当两个分支修改了同一文件的同一区域时:
# 合并时出现冲突
git merge feature-login
# Auto-merging app.py
# CONFLICT (content): Merge conflict in app.py
# 查看冲突文件
git status
# 编辑冲突文件,手动解决
# <<<<<<< HEAD
# print("Hello from main")
# =======
# print("Hello from feature")
# >>>>>>> feature-login
# 解决后标记为已解决
git add app.py
# 完成合并
git commit -m "Merge feature-login"
6. 远程仓库协作
6.1 远程仓库管理
# 添加远程仓库
git remote add origin https://github.com/user/repo.git
# 修改远程地址
git remote set-url origin https://github.com/user/new-repo.git
# 删除远程仓库
git remote remove origin
# 重命名远程
git remote rename origin upstream
# 查看远程仓库详细信息
git remote show origin
6.2 拉取更新
# git fetch:下载远程更新,但不合并
git fetch origin
git fetch --all
# git pull:下载并合并
git pull origin main
# 等价于: git fetch origin + git merge origin/main
# 使用 rebase 方式拉取
git pull --rebase origin main
6.3 跟踪远程分支
# 创建跟踪远程分支的本地分支
git checkout -b feature origin/feature
git branch -u origin/feature # 设置上游
# 查看跟踪关系
git branch -vv
6.4 解决推送冲突
当远程有新的提交而本地没有时:
# 方法一:先拉再推
git pull --rebase origin main # 将本地提交放在远程提交之后
git push origin main
# 方法二:强制推送(覆盖远程,谨慎使用)
git push --force-with-lease origin main # 更安全的强制推送
7. 撤销与回退
7.1 工作区撤销
# 丢弃工作区的修改(恢复到最后一次提交或暂存的状态)
git restore README.md
git checkout -- README.md # 旧语法
# 丢弃所有工作区修改
git restore .
7.2 暂存区撤销
# 将文件从暂存区移回工作区(不改变文件内容)
git restore --staged README.md
git reset HEAD README.md # 旧语法
# 取消所有暂存
git restore --staged .
7.3 提交撤销
# 撤销最近一次提交,但保留修改在工作区
git reset --soft HEAD~1
# 撤销最近一次提交,修改回到暂存区
git reset --mixed HEAD~1 # 默认行为
# 完全删除最近一次提交(丢弃修改)
git reset --hard HEAD~1
# 撤销最近 3 次提交
git reset --hard HEAD~3
HEAD 的含义:
HEAD:当前提交HEAD~1/HEAD^:上一次提交HEAD~2:上两次提交HEAD^^^:上三次提交
7.4 git revert - 安全撤销
reset 会修改历史,revert 会创建新提交来撤销变更:
# 创建一个新提交,内容与目标提交相反
git revert HEAD
git revert a1b2c3d
# 这在公共分支上更安全(不重写历史)
7.5 查看历史
# 基本日志
git log
# 一行显示
git log --oneline
# 图形化显示
git log --graph --oneline --all
# 查看每次修改的内容
git log -p
# 查看某个文件的修改历史
git log --follow README.md
# 查看谁改了某行
git blame README.md
8. 高级技巧
8.1 git stash - 临时保存
# 保存当前工作区修改
git stash
git stash push -m "WIP: 登录功能"
# 查看 stash 列表
git stash list
# 恢复最近一次 stash
git stash pop # 恢复并删除
git stash apply # 恢复并保留
# 恢复指定 stash
git stash apply stash@{1}
# 删除 stash
git stash drop stash@{0}
# 清空所有 stash
git stash clear
8.2 git rebase - 变基
Rebase 将一系列提交复制到另一个基点上:
# 将 feature 分支的提交放到 main 的最新提交之后
git checkout feature
git rebase main
# 交互式 rebase(合并、修改、删除提交)
git rebase -i HEAD~3
# 在交互式界面中:
# pick = 使用
# reword = 使用但修改消息
# edit = 使用但停止修改
# squash = 与前一个合并
# fixup = 与前一个合并(丢弃消息)
# drop = 删除
Rebase vs Merge:
| 操作 | Merge | Rebase |
|---|---|---|
| 历史 | 保留真实历史(有分叉) | 线性历史(无分叉) |
| 安全性 | 安全(不重写历史) | 危险(重写历史) |
| 适用场景 | 公共分支 | 私有分支 |
黄金法则: 不要对公共分支执行 rebase!
8.3 git cherry-pick - 选择性复制
# 复制某个提交到当前分支
git cherry-pick a1b2c3d
# 复制多个提交
git cherry-pick a1b2c3d e4f5g6h
# 复制但不自动提交
git cherry-pick -n a1b2c3d
8.4 git reflog - 救命稻草
Reflog 记录了所有 HEAD 的移动历史,即使你 reset 丢失了提交也能找回:
# 查看所有操作历史
git reflog
# a1b2c3d HEAD@{0}: commit: 添加新功能
# d4e5f6g HEAD@{1}: reset: moving to HEAD~1
# h7i8j9k HEAD@{2}: commit: 错误提交
# 恢复丢失的提交
git checkout h7i8j9k
git branch recovered-branch h7i8j9k
8.5 git bisect - 二分查找定位 Bug
8.5.1 什么是二分查找?
二分查找是一种在有序列表中快速定位目标的算法,时间复杂度为 O(log n)。Git 的 bisect 命令利用这一原理,在提交历史中快速定位引入 bug 的那一次提交。
为什么需要 git bisect?
假设你有 1000 次提交,已知:
- 第 1 次提交时程序正常
- 第 1000 次提交时程序有 bug
手动逐个检查需要测试 1000 次。而二分查找只需要测试约 10 次(log₂1000 ≈ 10)。
8.5.2 git bisect 工作原理
提交历史(按时间顺序):
1 --- 2 --- 3 --- 4 --- 5 --- 6 --- 7 --- 8 --- 9 --- 10
(good) (bad?)
bisect 过程:
第1步:测试中间提交 #5 → good(正常)
排除 1-5,范围缩小到 6-10
第2步:测试 #8 → bad(有 bug)
排除 9-10,范围缩小到 6-8
第3步:测试 #7 → bad
范围缩小到 6-7
第4步:测试 #6 → bad
结论:#6 是第一个 bad 提交
8.5.3 git bisect 基本用法
# 开始二分查找
git bisect start
# 标记当前版本为 bad(有 bug)
git bisect bad
# 标记已知的正常版本(可以用 commit hash、tag、或 HEAD~n)
git bisect good v1.0
git bisect good a1b2c3d
git bisect good HEAD~100 # 100 次提交前的版本
# Git 会自动切换到中间的一个提交
# Bisecting: 49 revisions left to test after this (roughly 6 steps)
# 测试当前版本(编译、运行、检查 bug)
# 如果当前版本正常:
git bisect good
# 如果当前版本有 bug:
git bisect bad
# 重复这个过程,直到找到第一个 bad 提交
# 找到后 Git 会显示:
# a1b2c3d is the first bad commit
# commit a1b2c3d...
# Author: ...
# Date: ...
#
# Fixed: 引入 bug 的提交消息
# 结束二分查找,回到原来的分支
git bisect reset
8.5.4 自动化 bisect
可以编写一个脚本自动测试,让 Git 自动运行二分查找:
# 创建一个测试脚本 test.sh
cat > test.sh << 'EOF'
#!/bin/bash
# 脚本应该返回 0 表示 good(正常),非 0 表示 bad(有 bug)
# 编译项目
make
# 运行测试
./run-tests
# 检查测试结果
if [ $? -eq 0 ]; then
exit 0 # good
else
exit 1 # bad
fi
EOF
chmod +x test.sh
# 自动二分查找
git bisect start
git bisect bad HEAD # 当前有 bug
git bisect good v1.0 # v1.0 正常
# 自动运行测试脚本
git bisect run ./test.sh
# Git 会自动找到第一个 bad 提交并输出
8.5.5 可视化 bisect 过程
# 查看 bisect 进度
git bisect visualize
# 或使用图形化工具
git bisect visualize --oneline --graph
8.5.6 查看 bisect 日志
# 查看 bisect 过程中标记的所有提交
git bisect log
# 输出示例:
# git bisect start
# # bad: [a1b2c3d] 最后一次提交
# git bisect bad a1b2c3d
# # good: [e4f5g6h] 初始版本
# git bisect good e4f5g6h
# # good: [i7j8k9l] 中间版本
# git bisect good i7j8k9l
8.5.7 保存和恢复 bisect 状态
# 保存当前 bisect 状态
git bisect log > bisect-state.txt
# 稍后恢复
git bisect start
git bisect replay bisect-state.txt
8.5.8 跳过无法测试的提交
有时某些提交无法编译或测试(比如构建系统损坏):
# 跳过当前提交
git bisect skip
# Git 会根据相邻提交的结果推断
9. Git 内部原理深度解析
9.1 对象存储详解
# 创建一个测试仓库
mkdir git-internals && cd git-internals
git init
# 创建一个文件
echo "Hello Git" > hello.txt
# 手动创建 blob 对象
git hash-object -w hello.txt
# 输出: 8a7b3c...(40 位哈希)
# 查看存储位置(前两位是目录名,后 38 位是文件名)
ls .git/objects/8a/
# 7b3c...(38 位)
# 查看对象类型
git cat-file -t 8a7b3c
# blob
# 查看对象内容
git cat-file -p 8a7b3c
# Hello Git
9.2 提交的完整过程
# 1. 添加文件到暂存区
git add hello.txt
# 此时 .git/index 记录:
# 100644 8a7b3c... 0 hello.txt
# 2. 查看暂存区内容
git ls-files --stage
# 100644 8a7b3c... 0 hello.txt
# 3. 创建提交
git commit -m "First commit"
# Git 内部做了什么:
# - 根据暂存区创建 Tree 对象
# - 创建 Commit 对象指向这个 Tree
# - 更新 .git/refs/heads/main 指向新 Commit
9.3 查看底层对象
# 查看最新提交的哈希
git rev-parse HEAD
# 输出: a1b2c3d4...
# 查看提交对象
git cat-file -p HEAD
# tree 0155eb...
# author 五味子 ...
#
# First commit
# 查看 tree 对象
git cat-file -p 0155eb
# 100644 blob 8a7b3c... hello.txt
# 查看 blob 对象
git cat-file -p 8a7b3c
# Hello Git
9.4 分支的本质(再次确认)
# 分支就是一个文件,存储了提交的哈希
cat .git/refs/heads/main
# a1b2c3d4e5f6...
# HEAD 是一个符号引用,指向当前分支
cat .git/HEAD
# ref: refs/heads/main
9.5 打包与压缩
Git 会定期将多个松散对象打包成一个 packfile 以节省空间:
# 手动打包
git gc
# 查看 packfile
ls .git/objects/pack/
# pack-abc123.idx
# pack-abc123.pack
# 验证仓库完整性
git fsck
10. 常见场景实战
场景 1:开始一个新项目
# 创建项目目录
mkdir my-awesome-project
cd my-awesome-project
# 初始化 Git
git init
# 创建 .gitignore(忽略不需要的文件)
echo "node_modules/
.env
*.log
.DS_Store" > .gitignore
# 创建 README
echo "# My Awesome Project" > README.md
# 首次提交
git add .
git commit -m "Initial commit"
# 关联远程仓库
git remote add origin https://github.com/username/my-awesome-project.git
# 推送到远程
git push -u origin main
场景 2:从 GitHub 克隆并开始开发
# 克隆仓库
git clone https://github.com/username/project.git
cd project
# 创建功能分支
git checkout -b feature/new-feature
# 开发...
echo "new code" >> src/app.py
git add src/app.py
git commit -m "实现新功能"
# 推送功能分支
git push -u origin feature/new-feature
# 在 GitHub 上创建 Pull Request
场景 3:同步远程更新
# 获取远程更新
git fetch origin
# 查看差异
git log main..origin/main
# 合并更新
git merge origin/main
# 或者用 rebase 保持线性历史
git pull --rebase origin main
场景 4:紧急修复 Bug
# 保存当前未完成的工作
git stash push -m "WIP: 正在开发登录功能"
# 切换到 main 分支
git checkout main
# 创建修复分支
git checkout -b hotfix/critical-bug
# 修复 bug
echo "fix" >> buggy.py
git add buggy.py
git commit -m "修复关键 bug"
# 合并到 main
git checkout main
git merge --no-ff hotfix/critical-bug
# 推送到远程
git push origin main
# 回到原分支继续开发
git checkout feature/login
git stash pop
场景 5:误删文件恢复
# 误删文件
rm important.py
git add -A
# 发现错误,还没有提交
git restore --staged important.py
git restore important.py
# 已经提交了删除
git log --oneline
# a1b2c3d Delete important.py
# 恢复
git revert a1b2c3d
# 或者用 reset
git reset --hard a1b2c3d^
场景 6:合并多个提交
# 查看最近 4 次提交
git log --oneline -4
# d4e5f6g 修复 typo
# c3d4e5f 添加日志
# b2c3d4e 调整格式
# a1b2c3d 实现功能
# 合并成 1 个提交
git rebase -i HEAD~4
# 在编辑器中:
# pick a1b2c3d 实现功能
# squash b2c3d4e 调整格式
# squash c3d4e5f 添加日志
# squash d4e5f6g 修复 typo
# 保存后编辑新提交消息
场景 7:撤销远程推送
# 撤销最后一次推送
git reset --hard HEAD~1
git push --force-with-lease origin main
# 注意:如果有其他人已经拉取了,会造成问题
# 更安全的方式是 revert
git revert HEAD
git push origin main
场景 8:查找引入 Bug 的提交
# 二分查找
git bisect start
git bisect bad HEAD # 当前版本有 bug
git bisect good a1b2c3d # 旧版本正常
# Git 会自动检出中间提交
# 测试后标记
git bisect good # 当前版本正常
git bisect bad # 当前版本有 bug
# 重复直到找到引入 bug 的提交
# 结束二分查找
git bisect reset
场景 9:使用二分查找定位 Bug(详细实战)
问题描述:
项目有 500 次提交,最近发现程序崩溃。v2.0 版本(150 次提交前)是正常的。
解决步骤:
# 1. 确保工作区干净
git status
# 2. 开始 bisect
git bisect start
# 3. 标记当前版本为 bad
git bisect bad
# 4. 标记已知正常版本
git bisect good v2.0
# 输出: Bisecting: 249 revisions left to test after this (roughly 8 steps)
# 5. Git 自动切换到一个中间提交
# 现在需要测试这个版本
# 6. 编译并测试
make clean && make
./run-tests
# 7. 根据测试结果标记
# 如果程序正常:
git bisect good
# 如果程序崩溃:
git bisect bad
# 8. 重复步骤 6-7,直到 Git 找到第一个 bad 提交
# 9. 查看结果
# a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 is the first bad commit
# commit a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
# Author: 开发者 <email>
# Date: 2024-01-15 14:30:00
#
# 重构了数据库连接模块
# 10. 查看该提交的具体改动
git show a1b2c3d
# 11. 修复 bug 或 revert 该提交
git revert a1b2c3d
# 12. 结束 bisect
git bisect reset
场景 10:使用自动化脚本批量 bisect
创建自动化测试脚本:
# 创建 test-runner.sh
cat > test-runner.sh << 'EOF'
#!/bin/bash
# 设置错误时退出
set -e
# 编译项目
echo "Compiling..."
make clean > /dev/null 2>&1
make > /dev/null 2>&1
# 运行单元测试
echo "Running tests..."
./bin/run-tests
# 检查测试结果
if grep -q "FAILED" test-results.txt; then
echo "BAD: Tests failed"
exit 1
else
echo "GOOD: All tests passed"
exit 0
fi
EOF
chmod +x test-runner.sh
# 运行自动化 bisect
git bisect start
git bisect bad HEAD
git bisect good v2.0
git bisect run ./test-runner.sh
# 输出示例:
# running ./test-runner.sh
# Compiling...
# Running tests...
# GOOD: All tests passed
# Bisecting: 124 revisions left to test after this (roughly 7 steps)
# [b2c3d4e] Fix: 修复配置加载
#
# running ./test-runner.sh
# Compiling...
# Running tests...
# BAD: Tests failed
# Bisecting: 61 revisions left to test after this (roughly 6 steps)
# [c3d4e5f] Feature: 添加缓存
#
# ...
#
# a1b2c3d is the first bad commit
场景 11:在多分支中定位 Bug
# 场景:bug 出现在 main 分支,但 feature 分支正常
git checkout main
git bisect start
# 标记 bad
git bisect bad
# 标记 feature 分支的最后一次提交为 good
git bisect good feature/login
# Git 会找出 main 分支相对于 feature 分支引入的第一个 bad 提交
场景 12:跳过损坏的提交
# 在 bisect 过程中遇到无法编译的提交
git bisect skip
# Git 会智能选择相邻的提交
# Bisecting: 30 revisions left to test after this (roughly 5 steps)
# [d4e5f6g] 临时提交(构建系统损坏)- skipped
# [e5f6g7h] 修复构建 - 自动测试此提交
场景 13:可视化 bisect 过程
# 在 bisect 过程中可视化
git bisect visualize --oneline --graph
# 输出示例:
# * a1b2c3d (HEAD, bad) 最新提交 - 有 bug
# * b2c3d4e 提交 2
# * c3d4e5f 提交 3
# | * d4e5f6g (good) v2.0 标签 - 正常
# |/
# * e5f6g7h 提交 5
# * f6g7h8i 提交 6
# Git 会在图中标出 good 和 bad 的位置
场景 14:bisect 与 git blame 结合
# 先用 bisect 定位到有 bug 的提交
git bisect start
git bisect bad HEAD
git bisect good v2.0
git bisect run ./test.sh
# 找到第一个 bad 提交:a1b2c3d
# 然后用 blame 查看该文件中具体哪一行引入了 bug
git blame -L 100,120 a1b2c3d^ -- src/buggy.py
# 或者查看该提交的具体改动
git show a1b2c3d
# 查看该提交中所有改动的文件
git diff a1b2c3d^..a1b2c3d --stat
场景 15:bisect 范围的精确控制
# 场景:已知 bug 出现在最近 50 次提交中
git bisect start
git bisect bad HEAD
git bisect good HEAD~50
# 场景:bug 出现在某个版本范围
git bisect start
git bisect bad v2.5
git bisect good v2.0
# 场景:排除某些分支(只在 main 分支上 bisect)
git bisect start --first-parent
# 场景:指定路径(只考虑影响特定文件的提交)
git bisect start -- src/buggy.py
git bisect bad
git bisect good v2.0
# Git 只会检出修改过 src/buggy.py 的提交
附录:常用命令速查表
基础命令
| 命令 | 说明 |
|---|---|
git init |
初始化仓库 |
git clone <url> |
克隆远程仓库 |
git status |
查看状态 |
git add <file> |
添加到暂存区 |
git commit -m "msg" |
提交 |
git push |
推送到远程 |
git pull |
拉取并合并 |
git fetch |
拉取不合并 |
分支命令
| 命令 | 说明 |
|---|---|
git branch |
查看分支 |
git branch <name> |
创建分支 |
git checkout <name> |
切换分支 |
git merge <branch> |
合并分支 |
git branch -d <name> |
删除分支 |
查看历史
| 命令 | 说明 |
|---|---|
git log |
查看提交历史 |
git log --oneline |
简洁历史 |
git log --graph |
图形化历史 |
git diff |
查看差异 |
git blame <file> |
查看每行责任人 |
撤销命令
| 命令 | 说明 |
|---|---|
git restore <file> |
丢弃工作区修改 |
git restore --staged <file> |
取消暂存 |
git reset --soft HEAD~1 |
撤销提交(保留修改) |
git reset --hard HEAD~1 |
撤销提交(丢弃修改) |
git revert HEAD |
创建反向提交 |
git stash |
临时保存 |
远程命令
| 命令 | 说明 |
|---|---|
git remote -v |
查看远程仓库 |
git remote add <name> <url> |
添加远程 |
git push -u <remote> <branch> |
推送并设置上游 |
git push --force-with-lease |
安全强制推送 |
bisect 命令速查表
| 命令 | 说明 |
|---|---|
git bisect start |
开始二分查找 |
git bisect bad [<ref>] |
标记当前/指定版本为 bad |
git bisect good [<ref>] |
标记当前/指定版本为 good |
git bisect skip [<ref>] |
跳过无法测试的提交 |
git bisect reset |
结束查找,回到原分支 |
git bisect visualize |
可视化查看进度 |
git bisect log |
查看操作日志 |
git bisect replay <file> |
从日志文件恢复状态 |
git bisect run <cmd> |
自动运行测试脚本 |
bisect 最佳实践
- 确保测试脚本是可靠的:脚本应该返回明确的 0/非 0
- 使用自动化脚本:对于大型项目,
git bisect run节省大量时间 - 保持提交原子性:每次提交应该能独立编译和运行
- 先缩小范围:如果知道大致范围,先用
good和bad缩小搜索区间 - 善用 skip:遇到无法测试的提交就跳过
- 记录结果:
git bisect log > bisect.log保存查找过程
总结:30 分钟成为 Git 专家的学习路径
第 0-5 分钟:理解核心概念
- Git 是分布式版本控制系统
- 三个区域:工作区 → 暂存区 → 仓库
- 文件状态:未跟踪/已修改/已暂存/未修改
第 5-15 分钟:掌握日常命令
- clone, add, commit, push, pull
- status, log, diff
- branch, checkout, merge
第 15-25 分钟:理解底层原理
- Git 是内容寻址的文件系统
- Blob、Tree、Commit 三种对象
- 分支只是指向 Commit 的指针
第 25-30 分钟:学习救急技巧
- reflog 找回丢失的提交
- reset vs revert 的区别
- stash 临时保存
记住这三个核心命令就够 80% 的场景:
git add . && git commit -m "message" && git push
遇到问题时:
- 先
git status查看当前状态 - 用
git log --oneline --graph查看历史 - 用
git reflog找回丢失的提交
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)