编程进阶网 编程进阶网
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接

杨充

专注编程 · 终身学习者
首页
  • 计算机原理
  • 操作系统
  • 网络协议
  • 数据库原理
  • 面向对象
  • 设计原则
  • 设计模式
  • 系统架构
  • 性能优化
  • 编程原理
  • 方案设计
  • 稳定可靠
  • 工程运维
  • 基础认知
  • 线性结构
  • 树与哈希
  • 工业级实现
  • 算法思想
  • 实战与综合
  • 算法题考核
  • C语言入门
  • C综合案例
  • C专栏博客
  • C标准集库
  • C++入门教程
  • C++综合案例
  • C++专栏博客
  • C++开发技巧
  • Java入门教程
  • Java综合案例
  • Java专栏博客
  • Go入门教程
  • Go综合案例
  • Go专栏博客
  • Go开发技巧
  • JavaScript入门
  • JavaScript高级
  • Android库解读
  • Android专栏
  • Android智能硬件
  • iOS ObjC入门
  • iOS Swift入门
  • iOS入门精通
  • Web之Html手册
  • Web之TypeScript
  • Web之Vue高级进阶
  • Linux之QML入门
  • Linux之QT核心库
  • Linux实践开发
  • Python教程
  • Shell&Bash教程
  • 工具脚本
  • 自动化脚本
  • 质量保障
  • 产品思考
  • 软实力
  • 开发流程
  • Git应用
  • 技术模版
  • 技术规范
  • Markdown
  • Mermaid
  • 开源协议
  • JSON工具
  • 文本工具
  • 图片处理
  • 文档转化
  • 代码压缩
  • 关于我
  • 自我精进
  • 职场管理
  • 职场面试
  • 心情杂货
  • 友情链接
  • README
  • 质量保障

  • 产品思考

  • 软实力

  • 开发流程

  • Git应用

    • README
    • 版本控制的诞生:从一场灾难说起
    • 单人工作流:在 Git 的时光机里自由穿梭
      • 一、我改了什么
      • 二、看的本质
        • 认知1:时间地图
        • 认知2:撤销目标
      • 三、时间地图
        • 3.1 基础用法
        • 3.2 可视化分支图
        • 3.3 只看 N 次提交
        • 3.4 看某个文件的历史
        • 3.5 搜索提交信息
        • 3.6 用日期范围过滤
        • 3.7 装个可视化增强插件(可选但推荐)
      • 四、精确定位
        • 场景 1:工作区 vs 版本库(还没 add 的修改)
        • 场景 2:暂存区 vs 版本库(add 之后、commit 之前)
        • 场景 3:两个 commit 之间
        • 动手感受「commit 之间 diff」的威力
      • 五、撤回工作区
        • 场景:改了几行,发现改错了,直接丢掉
      • 六、撤回暂存区
        • 场景:不小心 add 了一个不该提交的文件
      • 七、回退版本
        • 7.1 先准备实验环境
        • 7.2 --soft:撤销 commit,修改留在暂存区
        • 7.3 --mixed(默认):撤销 commit + 撤销暂存,修改留在工作区
        • 7.4 --hard:彻底删除(⚠️ 谨慎使用)
      • 八、终极后悔药
        • 8.1 亲手验证「丢了再找回来」
        • 8.2 reflog 的三个必备使用场景
      • 九、综合实战
        • 场景设定
        • 实战开始
        • 🎯 复盘定位
        • 🎯 恢复方案
        • 🎯 搞砸之后
      • 十、本章回顾
        • 📎 本章涉及的命令速查
    • 分支:Git 的灵魂——像开平行宇宙一样开发
    • 远程协作:把你的代码推到全世界
    • Git特种作战:stash、cherry-pick、bisect三件套
    • 团队工作流实战:从一个人能打到一队人能战
    • Git故障排除:遇到报错不再慌的急诊手册
    • Git 场景速查地图:遇到问题对号入座
    • 常见操作实践:从理论到实战的最后一步
  • 技术模版

  • 技术规范

  • markdown

  • mermaid

  • license

  • 博客部署

  • 技术招聘

  • 测试经验

  • 技术
  • Git应用
杨充
2025-06-06
目录

单人工作流:在 Git 的时光机里自由穿梭

# 第2章 · 单人工作流

# 一、我改了什么

上一章结尾,小李学会了 git init、add、commit。他兴奋地做了一整天的开发,提交了七八次。晚上回家前,他敲了 git log,看到一串 commit 记录,信心满满地合上了电脑。

第二天早上,他的同事小王在群里发了条消息:

"小李,昨天下午你改的那个 utils.js,把分页逻辑删掉了?现在第 2 页以后全报 404。"

小李心里一紧。他确实改过 utils.js,但不记得删过分页逻辑。更尴尬的是:

  • ❌ 他不记得在哪一次 commit 里改的
  • ❌ 他不记得改之前代码长什么样
  • ❌ 他想回到「加分页功能那次 commit」,但不记得它的 hash 是什么
  • ❌ 如果回退了,之后做的其他功能怎么办?

小李的问题不是 Git 能不能帮忙,而是他不知道用什么命令来「看」和「回」。

这就是本章要解决的问题:当你一个人写代码时,如何像翻书一样翻阅你的提交历史、精确查看每一次改动、以及随时退回到任意一个过去的版本。


# 二、看的本质

在深入命令之前,先建立两个关键认知:

# 认知1:时间地图

很多人把 git log 当成一条直线的时间线。错。在一个有很多分支的项目里,git log 输出的是一个有向无环图(DAG)——就像一张地铁线路图,每一条分支都是独立线路,有交叉、有分叉。

          o──o──o  feature/支付功能
         /
o──o──o──o──o  main
         \
          o──o──o  hotfix/登录崩溃
1
2
3
4
5

理解了这一点,你就不会再对 git log 的输出感到困惑——它不是在讲故事,而是在画地图。

# 认知2:撤销目标

Git 里的「撤销」不是一个单一动作,而是根据你要撤销的「东西在哪个区域」来决定的:

你要撤销的 东西在哪 用什么命令
还没 add 的修改 工作区 git restore / git checkout --
已经 add 但还没 commit 暂存区 git restore --staged / git reset HEAD
已经 commit 了 版本库 git reset --soft / --mixed / --hard
已经 push 了 远程仓库 git revert(第 5 章再讲)

本章我们集中解决前三个——也就是「单人本地工作」中的所有撤销场景。


# 三、时间地图

# 3.1 基础用法

mkdir git-log-lab && cd git-log-lab
git init

# 创建几个提交,模拟真实开发
echo "const app = 'My Blog';" > app.js
git add . && git commit -m "init: 项目初始化"

echo "function getPosts() { return []; }" >> app.js
git add . && git commit -m "feat: 添加获取文章列表函数"

echo "function renderPosts(posts) { console.log(posts); }" >> app.js
git add . && git commit -m "feat: 实现文章渲染"

echo "function deletePost(id) { /* 删除文章 */ }" >> app.js
git add . && git commit -m "feat: 添加删除文章功能"

echo "function editPost(id, content) { /* 编辑文章 */ }" >> app.js
git add . && git commit -m "feat: 添加编辑文章功能"

# 最基础的 log
git log
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

你会看到类似这样的输出:

commit 8f3a2b1c4d...  (HEAD -> master)
Author: 杨充 <yangchong@example.com>
Date:   Sun Jun 6 15:30:00 2025 +0800

    feat: 添加编辑文章功能

commit 7e2d1c0b9a...
Author: 杨充 <yangchong@example.com>
Date:   Sun Jun 6 15:29:00 2025 +0800

    feat: 添加删除文章功能

...
1
2
3
4
5
6
7
8
9
10
11
12
13

每次提交都有四个关键信息:谁、什么时候、为什么(message)、以及一个唯一的 hash。

但这样太啰嗦了。大部分时候你只想要一个概览:

# 一行一个 commit,简洁到极致
git log --oneline

# 输出:
# 8f3a2b1 feat: 添加编辑文章功能
# 7e2d1c0 feat: 添加删除文章功能
# 6c1b0a9 feat: 实现文章渲染
# 5b0a9f8 feat: 添加获取文章列表函数
# 4a9f8e7 init: 项目初始化
1
2
3
4
5
6
7
8
9

# 3.2 可视化分支图

git log --oneline --graph --all
1

在只有一条 branch 的时候,这看起来就是一条竖线,但当你有多个分支时(第 4 章),这条命令就是你的「导航地图」。

# 3.3 只看 N 次提交

git log --oneline -3          # 只看最近 3 次
1

# 3.4 看某个文件的历史

git log --oneline app.js      # 只显示 app.js 相关的提交
1

🎯 实战技巧:git log --oneline app.js 是你最常用的排查命令之一。当同事问「这个文件是谁改的、什么时候改的」,用这条命令一秒定位。

# 3.5 搜索提交信息

git log --oneline --grep="删除"    # 搜索 message 里包含"删除"的 commit
git log --oneline --grep="feat"    # 搜索所有 feat 类型提交
1
2

# 3.6 用日期范围过滤

git log --oneline --since="2025-06-01" --until="2025-06-07"
git log --oneline --since="2 days ago"
1
2

# 3.7 装个可视化增强插件(可选但推荐)

git log --all --graph --oneline --decorate
# 太长了?起个别名:
git config --global alias.lg "log --all --graph --oneline --decorate"
# 以后直接:
git lg
1
2
3
4
5

💡 这些 git log 选项不是让你死记硬背的。记三个最核心的就行:

  • git log --oneline:快速浏览
  • git log --oneline <文件名>:看文件历史
  • git log --oneline --graph --all:看全局分支图

其他的用到了再查。


# 四、精确定位

git log 告诉你「什么时候改了」,但 git diff 告诉你「到底改了什么」。

# 场景 1:工作区 vs 版本库(还没 add 的修改)

# 先在 app.js 第一行下面加一个注释
# (使用 echo 追加到文件)
echo "// 博客应用入口" >> app.js

# 看工作区和最新 commit 的差异
git diff
1
2
3
4
5
6

输出解读:

diff --git a/app.js b/app.js
index 1a2b3c4..5d6e7f8 100644       ← Git 内部的 hash 变化
--- a/app.js
+++ b/app.js
@@ -1,4 +1,5 @@                          ← -1,4 表示从第 1 行开始,显示 4 行(旧文件)
 const app = 'My Blog';                   ← 没有 +/- 前缀的是上下文
 function getPosts() { return []; }
 function renderPosts(posts) { console.log(posts); }
 function deletePost(id) { /* 删除文章 */ }
+// 博客应用入口                       ← + 是新增的行
1
2
3
4
5
6
7
8
9
10

+ 绿色的是你加进去的,- 红色的是你删掉的。 这一眼就能看出你做了什么。

# 场景 2:暂存区 vs 版本库(add 之后、commit 之前)

git add app.js

# 现在 git diff 什么都不显示了(因为工作区和暂存区一致)
git diff

# 想看暂存区和最新 commit 的区别,用 --cached(或 --staged)
git diff --cached
1
2
3
4
5
6
7

这正是我们在上一章学到的「三个区域」逻辑:git diff 比较「工作区 vs 暂存区」,git diff --cached 比较「暂存区 vs 版本库」。

# 场景 3:两个 commit 之间

# 比较两个 commit
git diff 4a9f8e7 8f3a2b1

# 比较当前工作区和两个版本之前的差异
git diff HEAD~2

# 只看某个文件在两个版本之间的变化
git diff HEAD~3 app.js
1
2
3
4
5
6
7
8

# 动手感受「commit 之间 diff」的威力

# 提交刚才的修改
git commit -m "docs: 添加注释"

# 现在比较「初始化那次」和「现在」,看看 app.js 长什么样
git diff $(git log --oneline | tail -1 | awk '{print $1}') HEAD -- app.js
1
2
3
4
5

你会看到从项目初始化到现在,app.js 的每一行增量变化。

💡 核心认知:diff 不难,难的是搞清楚 「跟谁比」。记住这个口诀:

  • 不加参数 → 工作区 vs 暂存区(还没 add 的改动)
  • 加 --cached → 暂存区 vs 版本库(add 了但还没 commit 的)
  • 加 commit hash → 指定版本 vs 当前状态

# 五、撤回工作区

# 场景:改了几行,发现改错了,直接丢掉

# 在 app.js 里随便加点"垃圾代码"
echo "asdfasdfasdf" >> app.js

# 看改了什么
git diff app.js
# 输出显示多了 asdfasdfasdf

# 😱 算了,不要了!
git restore app.js    # 新版命令(Git 2.23+)
# 等效旧命令:git checkout -- app.js

# 确认:没了
git diff app.js
# 什么都没输出 → 干净了
1
2
3
4
5
6
7
8
9
10
11
12
13
14

同时撤销所有文件:

git restore .
1

⚠️ 这是一条不会给你反悔机会的命令。 restore 直接丢弃工作区的修改,被丢弃的改动不会出现在 reflog 里,因为从未 commit。用之前务必 git diff 确认一下。


# 六、撤回暂存区

# 场景:不小心 add 了一个不该提交的文件

# 创建两个文件
echo "重要功能" > feature.js
echo "password123" > secrets.txt   # 不该提交的敏感信息!

# 一个没注意,全 add 了
git add .

git status
# 输出:
# Changes to be committed:
#   new file: feature.js
#   new file: secrets.txt    ← 危险!这是密码!

# 把 secrets.txt 从暂存区撤回来
git restore --staged secrets.txt
# 等效旧命令:git reset HEAD secrets.txt

git status
# 输出:
# Changes to be committed:
#   new file: feature.js      ← 只有它了
# Untracked files:
#   secrets.txt                ← 回到工作区,没有丢失
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

撤回了暂存区,文件内容没丢——它只是回到了「未跟踪」或「已修改」状态。


# 七、回退版本

这是 Git 中最让人困惑但也最强大的命令。reset 有三个模式,区别在于回退之后,你的修改在哪里。

# 7.1 先准备实验环境

# 把 feature.js 提交一下
git add feature.js
git commit -m "feat: 添加核心功能模块"

# 看看当前历史
git log --oneline
# 假设输出:
# a1b2c3d feat: 添加核心功能模块
# 1d2e3f4 docs: 添加注释
# 8f3a2b1 feat: 添加编辑文章功能
# ...省略...
1
2
3
4
5
6
7
8
9
10
11

# 7.2 --soft:撤销 commit,修改留在暂存区

git reset --soft HEAD~1

git log --oneline
# a1b2c3d 消失了!HEAD 退到了 1d2e3f4

git status
# 输出:
# Changes to be committed:
#   new file: feature.js      ← 修改还在!在暂存区等你重新 commit
1
2
3
4
5
6
7
8
9

🎯 适用场景:commit message 写错了想改,或者 commit 了之后发现少了一个文件想补进去。

# 7.3 --mixed(默认):撤销 commit + 撤销暂存,修改留在工作区

# 重新 commit 一次,方便继续实验
git commit -m "feat: 添加核心功能模块"

# 这次用 mixed
git reset --mixed HEAD~1
# 或直接 git reset HEAD~1(默认就是 --mixed)

git log --oneline
# a1b2c3d 又消失了

git status
# 输出:
# Untracked files:
#   feature.js        ← 修改还在,但回到了未跟踪状态(工作区)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

🎯 适用场景:commit 了之后想重新组织——把这次提交拆成多个更小的 commit,或者把这次改动合并到下一个 commit。

# 7.4 --hard:彻底删除(⚠️ 谨慎使用)

# 再次重新 commit
git commit -m "feat: 添加核心功能模块"

# 这次用 hard
git reset --hard HEAD~1

git log --oneline
# a1b2c3d 消失了

git status
# nothing to commit, working tree clean
# ← feature.js 和它的修改全都消失了!
1
2
3
4
5
6
7
8
9
10
11
12

你会看到 feature.js 文件都找不到了——不仅 commit 没了,文件本身也没了。

            --soft          --mixed          --hard
  commit     ✗ 消失          ✗ 消失           ✗ 消失
  暂存区     ✓ 保留          ✗ 回到工作区      ✗ 消失
  工作区     ✓ 保留          ✓ 保留           ✗ 消失
1
2
3
4

⚠️ 三选一指南:

  • 只想修改上次 commit message → --soft(最安全)
  • 想重新规划这次改动的 commit 结构 → --mixed(默认,安全)
  • 确定这次所有的修改都不要了 → --hard(危险,但有后悔药)

# 八、终极后悔药

你可能会问:--hard 不是全删了吗?如果我 reset --hard 之后后悔了怎么办?

这就是 reflog 的用武之地——它记录了 HEAD 的每一次位置变化,不管你怎么 reset,它都帮你记着。

# 8.1 亲手验证「丢了再找回来」

# 先看看当前 reflog
git reflog
# 输出类似:
# 1d2e3f4 HEAD@{0}: reset: moving to HEAD~1
# a1b2c3d HEAD@{1}: commit: feat: 添加核心功能模块
#           ↑ 看到了!那个被 --hard 删掉的 commit 还在!

# 找回它!
git reset --hard a1b2c3d

# 确认:feature.js 回来了
ls feature.js
# 输出:feature.js ← 回来了!
1
2
3
4
5
6
7
8
9
10
11
12
13

# 8.2 reflog 的三个必备使用场景

# 场景1:reset --hard 之后后悔
git reflog
# 找到 reset 之前的 commit hash
git reset --hard <hash>

# 场景2:误删分支
git reflog
# 找到分支在删除前指向的 commit
git checkout -b <恢复的分支名> <hash>

# 场景3:查看 30 天内的操作记录
git reflog --date=iso
# 每条记录都带精确时间戳
1
2
3
4
5
6
7
8
9
10
11
12
13

💡 Git 最重要的安全网:只要做过 commit,Git 默认保留 30 天。30 天内无论你删分支、reset --hard、还是任何骚操作,reflog 都能救你。


# 九、综合实战

# 场景设定

你是小李,你要为一个电商项目实现「商品管理」功能。按照最佳实践,你计划小步提交。但中途你引入了一个 bug,需要定位并撤销。

# 实战开始

# ==================== Phase 1:搭建项目基础 ====================
mkdir shop && cd shop
git init

echo 'const products = [];' > products.js

echo 'function addProduct(name, price) {
  products.push({ name, price });
}' >> products.js

git add . && git commit -m "feat: 初始化商品模块"


# ==================== Phase 2:实现查询功能 ====================
echo '
function findProduct(name) {
  return products.find(p => p.name === name);
}' >> products.js

git add . && git commit -m "feat: 添加商品查询功能"

# 假设 hash: b111111


# ==================== Phase 3:实现删除功能 ====================
echo '
function deleteProduct(name) {
  const idx = products.findIndex(p => p.name === name);
  if (idx !== -1) products.splice(idx, 1);
}' >> products.js

git add . && git commit -m "feat: 添加商品删除功能"

# hash: c222222


# ==================== Phase 4:实现统计功能(这里引入了一个 bug) ====================
echo '
function getTotalPrice() {
  let total = 0;
  for (let p of products) total += p.price;
  return total;  // ← 这里是对的
}' >> products.js

git add . && git commit -m "feat: 添加总价统计功能"

# hash: d333333


# ==================== Phase 5:越改越糟 ====================
# 同事说结果要保留两位小数,小李随手一改...
# 我们用 sed 把 "return total;" 换成 "return total.toFixed(2);"
# 但 .toFixed(2) 返回的是字符串,后续计算会出问题
sed -i '' 's/return total;/return total.toFixed(2);/' products.js

git add . && git commit -m "fix: 统计结果保留两位小数"

# hash: e444444


# ==================== Phase 6:又加了新功能 ====================
echo '
function getProductCount() {
  return products.length;
}' >> products.js

git add . && git commit -m "feat: 添加商品数量统计"

# hash: f555555
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

现在小李的项目历史长这样:

f555555  feat: 添加商品数量统计         ← HEAD(当前位置)
e444444  fix: 统计结果保留两位小数       ← 🙈 引入类型 bug
d333333  feat: 添加总价统计功能          ← ✨ 这里是好的
c222222  feat: 添加商品删除功能
b111111  feat: 添加商品查询功能
a000000  feat: 初始化商品模块
1
2
3
4
5
6

# 🎯 复盘定位

第二天早上,小李的前端同事说 getTotalPrice() 返回的结果不能直接参与计算。小李要通过 Git 查清楚发生了什么。

Step 1:看看历史

git log --oneline
1

Step 2:缩小范围,只看 products.js 的变化

git log --oneline -- products.js
1

Step 3:找到引入 bug 的 commit

只看 message 就知道 e444444 fix: 统计结果保留两位小数 最可疑。验证一下:比较它和它之前的版本:

git diff e444444~1..e444444 -- products.js
1

输出:

-  return total;
+  return total.toFixed(2);
1
2

找到了!toFixed(2) 返回字符串,破坏了类型。

# 🎯 恢复方案

方案 A:git revert(安全,适合已 push 的场景)

git revert e444444 -m "revert: 撤销保留两位小数的修改,toFixed 破坏类型"
# revert 创建一个新 commit,反向抵消 e444444 的改动

git log --oneline
# 输出:
# g666666 revert: 撤销保留两位小数的修改...   ← 新增的抵消 commit
# f555555 feat: 添加商品数量统计
# e444444 fix: 统计结果保留两位小数
# ...
# e444444 的改动被抵消了,但 commit 还在历史里
1
2
3
4
5
6
7
8
9
10

方案 B:git rebase -i(适合未 push,想彻底删除那个 commit)

# 先用 revert 撤销回去,好继续实验
git reset --hard g666666     # 回到 revert 后的状态
# ... 这里我们重新开始另一个方案 ...
1
2
3

等等,让我们重新来。先回到 Phase 6 结束的状态:

git reflog
# 找到 "feat: 添加商品数量统计" 的 hash(f555555)
git reset --hard f555555
1
2
3

现在用交互式 rebase 删除 e444444(那个有 bug 的 commit):

git rebase -i e444444~1
1

会弹出一个编辑器,显示:

pick e444444 fix: 统计结果保留两位小数
pick f555555 feat: 添加商品数量统计
1
2

把要删除的那一行 pick 改成 drop(或直接删掉这一行):

drop e444444 fix: 统计结果保留两位小数    ← 删掉
pick f555555 feat: 添加商品数量统计
1
2

保存退出。看结果:

git log --oneline
# 输出:
# f555555_feat: 添加商品数量统计       ← 重新生成了,hash 变了
# d333333 feat: 添加总价统计功能        ← 干净的!
# c222222 feat: 添加商品删除功能
# b111111 feat: 添加商品查询功能
# a000000 feat: 初始化商品模块
1
2
3
4
5
6
7

e444444 直接消失了,就像从未存在过。

# 🎯 搞砸之后

# rebase 过程中出现问题,不想继续了
git rebase --abort

# rebase 完了发现不对,用 reflog 找回
git reflog
# 找到 rebase 之前的那个 f555555(原始的)
git reset --hard <原始hash>
1
2
3
4
5
6
7

# 十、本章回顾

你的需求 用什么命令 口诀
快速看一眼历史 git log --oneline 一行看全
看全局分支图 git log --oneline --graph --all 地图模式
看某个文件的历史 git log --oneline -- <file> 锁定文件
看还没 add 的改动 git diff 改了啥?
看 add 了但没 commit 的 git diff --cached 要交啥?
看两个版本的区别 git diff <hash1> <hash2> 版本间找差异
丢一个文件还没 add git restore <file> 不要了
add 错了想撤回 git restore --staged <file> 暂存撤回
改 commit message git reset --soft HEAD~1 软回退
重新规划 commit 结构 git reset --mixed HEAD~1 混合回退
彻底丢弃这次 commit git reset --hard HEAD~1 硬回退 ⚠️
reset 错了想反悔 git reflog → git reset --hard <hash> 后悔药
已 push 了想撤销 git revert <hash> 反向抵消

🚀 核心心法(本章三句话):

  1. git log 是地图,git diff 是放大镜——先定位,再细看。
  2. 撤销不恐怖,恐怖的是你在哪个区域反应不过来——对照三区域四状态再动手。
  3. git reflog 是你的终极保险——什么都能丢,commit hash 别丢。

🏃 下一章预告:你一个人已经能自由穿梭历史了。接下来,Git 真正的灵魂登场——分支。你会学到:为什么分支只是一个 40 字节的指针、merge 和 rebase 到底怎么选、以及那个让所有新手崩溃的「冲突」到底怎么优雅地解决。


# 📎 本章涉及的命令速查

# === 时间地图(看历史) ===
git log                             # 完整历史
git log --oneline                   # 一行一个 commit
git log --oneline --graph --all     # 可视化分支图
git log --oneline -3                # 最近 3 次
git log --oneline <file>            # 只看某个文件的历史
git log --oneline --grep="关键词"   # 按 message 搜索
git log --oneline --since="2 days ago"  # 按日期过滤

# === 放大镜(看差异) ===
git diff                        # 工作区 vs 暂存区
git diff --cached               # 暂存区 vs 版本库
git diff <hash1> <hash2>        # 两个版本之间
git diff HEAD~2                 # 当前 vs 两代之前

# === 撤销工作区 ===
git restore <file>              # 丢弃工作区修改(⚠️ 无法恢复)
git restore .                   # 丢弃所有工作区修改

# === 撤销暂存区 ===
git restore --staged <file>     # 从暂存区撤回(文件不丢)

# === 回退版本 ===
git reset --soft HEAD~1         # 撤销 commit,修改在暂存区
git reset --mixed HEAD~1        # 撤销 commit+暂存,修改在工作区
git reset --hard HEAD~1         # 全部丢弃(⚠️ 但 reflog 可救)

# === 后悔药 ===
git reflog                      # 查看 HEAD 移动记录
git reflog --date=iso           # 带时间戳的后悔药

# === 安全撤销(已 push) ===
git revert <hash>               # 新建一个 commit 抵消指定改动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#Git
上次更新: 2026/06/07, 10:26:12
版本控制的诞生:从一场灾难说起
分支:Git 的灵魂——像开平行宇宙一样开发

← 版本控制的诞生:从一场灾难说起 分支:Git 的灵魂——像开平行宇宙一样开发→

最近更新
01
信号崩溃快速排查
06-15
02
CoreDump破案
06-15
03
perf火焰图实战
06-15
更多文章>
Theme by Vdoing | Copyright © 2019-2026 杨充 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式