1188

Git 代码回退 / 回滚命令速查

场景:代码改坏了、想撤销某次提交、想放弃未提交的改动。 本文档结合本项目实际案例整理,按「危险程度」从低到高排列。

一、先搞清楚三个概念

术语含义对应状态工作区你正在编辑的文件git status 里 M 开头的就是暂存区(index)git add 之后git status 里绿色的是**历史(HEAD)**已 git commit 的记录git log 能看到

核心心法:回退前先想清楚——你要撤销的是"未提交的改动"还是"已经提交/推送的提交"?这两类处理方法完全不同。

二、撤销「未提交」的改动(最常用、最安全)

2.1 丢弃工作区的修改(还没 add)

# 丢弃单个文件

git checkout -- app/components/Markdown.tsx

# 丢弃所有文件的修改

git checkout -- .

# 或

git restore .

2.2 丢弃已 add 但未 commit 的改动

# 从暂存区移回工作区(文件内容不变)

git restore --staged app/components/Markdown.tsx

# 同时丢弃暂存 + 工作区的修改

git checkout HEAD -- app/components/Markdown.tsx

⚠️ 以上命令永久丢弃未提交的改动,无法找回。如果改动很重要,先 git stash 暂存起来:

git stash # 暂存所有未提交改动(可以反悔)

git stash pop # 恢复刚才暂存的改动

git stash list # 查看暂存列表

三、撤销「已提交」但「未推送」的提交(本地 commit)

3.1 reset 软回退(推荐,保留改动在工作区)

# 撤销最近 1 次提交,改动回到暂存区(文件内容还在,可以重新提交)

git reset --soft HEAD~1

# 撤销最近 1 次提交,改动回到工作区(未 add 状态)

git reset HEAD~1 # 等同 --mixed

3.2 reset 硬回退(彻底删除提交和改动)

# 彻底回退到上一版本,丢弃这次提交的所有改动(慎用!)

git reset --hard HEAD~1

💡 HEAD~1 = 上一个提交,HEAD~3 = 上三个,也可以直接用提交 hash:git reset --hard abc1234。

四、撤销「已推送」的提交(本次项目实际案例)⭐

场景:CSDN 风格的 Markdown 排版提交 150085e 已经 push 到 GitHub / Vercel 已部署,后来决定不要了,要回退到上一版本。

4.1 推荐:git revert(生成反向提交,不改写历史)

# 撤销某个已推送的提交(自动生成一个"反向"的新提交)

git revert 150085e

# 推送回远程,Vercel 自动重新部署

git push my-blog main

特点:

✅ 历史完整保留(面试讲起来是"可审计的")

✅ 普通 push 即可,不需要 force

✅ 其他人/CI 不会因为历史改写而冲突

✅ 可以安全地针对任意一个已推送提交(不限于最后一个)

本次实际执行:

git revert --no-edit 150085e

# 生成 [main 798701d] Revert "style: Markdown 排版改为 CSDN 风格..."

git push my-blog main # 150085e..798701d main -> main

4.2 备选:reset + force push(改写历史,慎用)

# 回退到上一版本(本地)

git reset --hard HEAD~1

# 强制推送覆盖远程(危险!会改写远程历史)

git push --force my-blog main

# 或更安全的形式(防止覆盖别人新推的提交):

git push --force-with-lease my-blog main

特点:

⚠️ 改写了远程历史,禁止用于协作分支(多人共用 main 时会导致别人冲突)

⚠️ force push 后,Vercel 可能因为"提交未验证"等问题出幺蛾子

✅ 适合:单人项目、明确要抹掉某段历史的场景

💡 为什么本项目用 revert 而不是 reset --hard + force push? 因为历史已经被推送到远程且 Vercel 已部署——改写历史会破坏 Vercel 的部署追踪、GitHub 的提交记录,而且完全没必要。revert 达到同样的"代码回到上一版"效果,代价是历史里多一条 Revert 提交,干净且安全。

五、回退到"某次提交"的多个常用命令

git log --oneline -10 # 查看最近提交(拿 hash)

git show 150085e --stat # 看某次提交改了什么文件

git revert # 撤销任意一次提交(推荐)

git reset --hard # 硬回退到某次提交(丢后面的所有提交,慎用)

git diff HEAD~1 --stat # 看上一个提交和现在的差异

六、本次案例完整复盘(项目实际场景)

背景:写文章页 Markdown 排版时,先做了"专业排版重写"(提交 0e5f2b3),又按 CSDN 风格重写(提交 150085e)。用户觉得 CSDN 版本不好看,要求回退到上一版。

实际执行过程:

# 1. 先看本地状态——发现 CSDN 提交其实已经 push 到远程了

git fetch my-blog && git status -sb

# 输出: ## main...my-blog/main (本地与远程同步,说明远程已是 CSDN 版)

# 2. 确认要回退的提交内容

git show 150085e --stat

# 输出: 3 files changed(Markdown.tsx / CodeBlockCopy.tsx / globals.css)

# 3. 生成反向提交(revert)

git revert --no-edit 150085e

# 输出: [main 798701d] Revert "style: Markdown 排版改为 CSDN 风格..."

# 4. 推送,Vercel 自动部署回退版

git push my-blog main

# 输出: 150085e..798701d main -> main

结果:线上回到 CSDN 之前的排版,历史完整保留,无 force push。

七、命令速查表

需求命令丢弃未提交的单个文件改动git checkout -- 丢弃所有未提交改动git checkout -- .暂存未提交改动(可反悔)git stash / git stash pop撤销未推送的提交(保留改动)git reset --soft HEAD~1彻底撤销未推送的提交git reset --hard HEAD~1撤销已推送的提交(推荐)git revert + push强制覆盖远程(慎用)git push --force-with-lease查看提交历史git log --oneline看某次提交改了什么git show --stat

八、避坑要点

先 git status / git log 再动手——确认改动是否已提交/推送,方法完全不同

git checkout -- 和 git reset --hard 不可逆——重要改动先 git stash

多人协作的项目禁用 force push——用 git revert

revert 是"再加一条提交",不是删历史——所以 push 不会失败,CI/Vercel 都正常

远程已部署过的东西想回退,永远优先 revert——reset+force 会让 Vercel 部署追踪混乱