Git 完全指南:从入门到原理精通

目录

  1. Git 是什么
  2. Git 的核心概念与工作原理
  3. 安装与配置
  4. 基础操作(clone, add, commit, push)
  5. 分支管理
  6. 远程仓库协作
  7. 撤销与回退
  8. 高级技巧
  9. Git 内部原理深度解析
  10. 常见场景实战

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 的原理:

  1. 计算文件内容的 SHA-1 哈希
  2. 将文件内容压缩后存入 .git/objects 作为 Blob 对象
  3. 将文件路径和 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 的原理:

  1. 读取 .git/index 中的暂存区信息
  2. 创建一个 Tree 对象(目录结构快照)
  3. 创建 Commit 对象:
    • Tree 哈希
    • 父 Commit 哈希(HEAD 指向的)
    • 作者信息
    • 时间戳
    • 提交消息
  4. 将当前分支的引用(如 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 的原理:

  1. 与远程仓库建立连接(HTTPS/SSH)
  2. 交换引用信息(双方有哪些 Commit)
  3. 传输本地有但远程没有的 Commit 对象
  4. 更新远程分支指向最新的 Commit
  5. 快进合并(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 最佳实践

  1. 确保测试脚本是可靠的:脚本应该返回明确的 0/非 0
  2. 使用自动化脚本:对于大型项目,git bisect run 节省大量时间
  3. 保持提交原子性:每次提交应该能独立编译和运行
  4. 先缩小范围:如果知道大致范围,先用 goodbad 缩小搜索区间
  5. 善用 skip:遇到无法测试的提交就跳过
  6. 记录结果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

遇到问题时:

  1. git status 查看当前状态
  2. git log --oneline --graph 查看历史
  3. git reflog 找回丢失的提交
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐