Git速查表

按撤销、回退、变基、找 bug、清理几类场景列出 30 条常用命令,每条都标明会不会改写历史,可搜索、可按分组筛选、可导出 CSV。

场景分组(可多选,至少要留一个)

场景分组对照表

默认展示搜索结果的 12 行;搜索框清空后显示全部 30 条的前 12 行,点「加载更多」可以补齐剩下的 18 行。

场景命令作用会不会改写历史备注
撤销与回退git reset --soft HEAD~n撤销 n 次提交,改动退回暂存区改写本地历史reflog 可回溯
撤销与回退git reset HEAD~n撤销提交并取消暂存改写本地历史默认即 --mixed
撤销与回退git reset --hard HEAD~n撤销提交并丢弃全部改动改写本地历史改动找不回
撤销与回退git commit --amend改最近一次提交的信息或内容改写本地历史ID 会变
撤销与回退git reset --hard ORIG_HEAD回到上次 reset 前的 HEAD改写本地历史救回误操作
变基与合并git rebase main把本分支提交搬到 main 之上改写本地历史公共分支勿用
变基与合并git rebase -i HEAD~n交互式整理最近 n 个提交改写本地历史压提交主力
变基与合并git rebase --onto main base topic搬运指定区间的提交改写本地历史精确控制范围
变基与合并git pull --rebase拉取时用变基而非合并改写本地历史本地提交被重放
清理与危险git push --force-with-lease强推前比对远端引用改写远程历史协作场景首选
清理与危险git push --force无条件覆盖远端分支改写远程历史丢掉别人的提交
清理与危险git filter-repo --path <path>从全部提交里彻底删除文件改写全部历史协作者需重新克隆

提示:搜索框与 n 输入框里按 Enter 直接筛选;改动任一控件会自动重算。所有命令只是文本,本页不会、也无法在你的机器上执行任何 git 命令。

使用方法

  1. 这个 Git速查表打开时搜索框里已经填了 历史,用来演示按口径筛选:「会不会改写历史」一列里带判定文字的命令会被挑出来,正好 12 条,也就是全部 30 条里会改动提交历史的那些。
  2. 把搜索框改成 reflog、撤销、bisect 之类的关键词,场景、命令、作用、判定四列都会参与匹配,大小写不敏感;清空搜索框即恢复全部 30 条。
  3. 右边「回退步数 n」填 1(示例值),结果区会用这个 n 生成四条 HEAD~n 命令预览,方便直接复制。允许 1~50 的整数。
  4. 四个场景分组默认全勾:撤销与回退 9 条、变基与合并 6 条、查找与调试 7 条、清理与危险 8 条。只关心某一类时取消其余勾选即可。
  5. 在搜索框或 n 输入框里按 Enter 等于点「筛选命令」;「复制对照表」把当前结果以 TSV 送进剪贴板,「导出 CSV」生成带 BOM 的表格文件。改 n 只影响上面的命令预览,不影响表格内容。

Git速查表能解决什么问题

一是在动手之前先看清风险。网上抄来一条命令就敲,是这个工具最想拦住的场景。同样叫「撤销提交」,git revert 只是新增一个反向提交、历史只增不减,而 git reset --hard 会把 HEAD 往回搬、顺手丢掉工作区改动。本页把每条命令的判定写在「会不会改写历史」一列,先看那一列再决定敲不敲。

二是区分本地改写与远端改写。改写本地历史只影响你自己的仓库,git reset、git rebase、git commit --amend 都属于这一类;一旦推送到公共分支,就需要 git push --force 才能覆盖远端,而它会直接丢掉同事已经推送的提交。表格里把这两类分开标注,是这一列最关键的区分。

三是找回被误操作丢掉的东西。误执行 git reset --hard 之后,提交并没有立刻消失,只是从分支上不可达了:git reflog 默认保留 90 天,git reset --hard ORIG_HEAD 能把 HEAD 搬回去,git fsck --lost-found 还能捞回悬空对象。本页把这几条放在同一组,出事了按顺序试。

四是把二分查找当成常规手段。缺陷不知道是哪次提交引入的时候,git bisect 比逐个 checkout 快得多,1000 个提交的区间最多 10 步就能定位。这一组命令全部是只读操作,不改写任何历史,可以放心用。

本页不做这些事:不执行任何 git 命令、不连接你的仓库、不解析 .git 目录,只是一份带检索的文本速查表;也不讲 Git 的安装与远程仓库配置。要找某个正则写法请用页面底部的「正则速查表」,要查 HTTP 状态码含义请用「HTTP状态码代码速查表」。

判定原理:什么才算「改写历史」

要判断一条命令危不危险,先要知道 Git 把数据存在哪里。提交并不是「一个可以就地修改的文件夹」,而是内容寻址的不可变对象:对象内容的 SHA-1 摘要就是它的 ID。实际算法是 SHA-1("blob " + 内容字节数 + "\0" + 内容),本页复算过——对内容 hello(5 字节)求摘要得到 b6fc4c620b67d95f953a5c1c1230aaab5db5a1b0;仅仅在末尾多一个换行,结果就变成 ce013625030ba8dba906f756967f9e9ca394464a,与前者毫无相似之处。SHA-1 输出 40 个十六进制字符、160 bit;正在推进的 SHA-256 版本会变成 64 个字符、256 bit。

因为提交对象里保存着父提交的 ID,改任何一个字节都会连锁改变它以及它所有后代的 ID。这就解释了为什么 git commit --amend 不是「改一下提交信息」那么轻——它生成的是一个全新的提交对象;也解释了 git rebase 为什么叫「重放」:把一条分支上的 12 个提交搬到新基底上,会产生 12 个全新的提交对象,12 个 ID 全部变样,旧的那 12 个只在 reflog 里还可达。

所以本页的判定标准只有一条:这条命令会不会让已经存在的提交从分支引用上脱离可达。会,就标「改写本地历史」;只影响远端引用的标「改写远程历史」;从头到尾不移动任何引用的标「不改写」;连对象库都不写的标「只读」。分支和标签本身只是仓库里一个记录提交 ID 的引用文件,内容就是 40 个十六进制字符加一个换行、共 41 字节,所以 git branch -D 删掉的是引用而不是提交,对象还在,这也是删错分支后还能救回来的原因。

反向操作也有代价边界:git reset --hard 丢弃的是工作区里从未提交过的改动,这部分内容从来没有进过对象库,reflog 里也没有记录,找不回来;而已经提交过的内容即使变成悬空对象,也能在 git 的默认回收策略生效前捞回来。默认值是 gc.reflogExpire 90 天、gc.reflogExpireUnreachable 30 天,一旦手工执行 git gc --prune=now,悬空对象可能当场消失。这就是本页把 gc 单独标成「改写仓库存储」而不是「只读」的原因。

完整示例:撤销一次已经推送出去的提交

场景:提交已经推到公共分支 main,现在要撤销它。页面默认的 n = 1 表示最近一次提交,结果区给出的预览就是下面这四条,取舍如下:

把这段取舍写成可执行的顺序,就是本页想表达的用法:先看「会不会改写历史」那一列,再决定用 revert 还是 reset。判断依据还是对象 ID——revert 产生的是一个父提交指向当前 HEAD 的新提交,原提交仍然可达,所以协作者的仓库不需要任何特殊处理;reset 是移动分支引用,原提交从分支上脱离,远端要同步就必须强推,而强推用 --force-with-lease 比 --force 安全:前者会先比对远端引用是否被你之外的提交改动过,后者无条件覆盖。

做法会不会改写历史已推送分支能不能用丢数据风险
git revert不改写可以,推荐无
git rebase -i改写本地历史要强推,不建议无(提交内容还在)
git reset --soft改写本地历史要强推,不建议无(改动留在暂存区)
git reset --hard改写本地历史要强推,不建议丢弃未提交改动
git push --force-with-lease改写远程历史可用但需先比对覆盖他人的提交
git push --force改写远程历史可用但危险直接丢掉他人的提交

场景分组与危险等级对照表

全部 30 条命令按判定分布如下。「只读」和「不改写」加起来有 17 条,超过一半——日常排查和清理并不需要动历史,真正会改动提交历史的只有 13 条。

会不会改写历史条数典型命令什么时候用
只读9git log / blame / bisect / fsck排查与定位,随便用
不改写8git revert / cherry-pick / stash已推送分支上的常规操作
改写本地历史9git reset / rebase / commit --amend只在还没推送的本地分支上做
改写远程历史2git push --force / --force-with-lease确认没人依赖该分支的提交后
改写全部历史1git filter-repo清理误提交的密钥或大文件,需全员重新克隆
改写仓库存储1git gc --prune=now清理悬空对象前想清楚还要不要它们

四个场景分组各有多少条、各自的关键命令是什么:

场景分组条数本组最常用的命令本组标注「改写」的条数
撤销与回退9git reset --soft / git revert / git reflog5
变基与合并6git rebase -i / git cherry-pick4
查找与调试7git bisect / git blame / git log -S0
清理与危险8git clean -fd / git push --force-with-lease4

输入范围、边界与安全边界

常见问题

为什么这一列要单列「会不会改写历史」,而不是只标危险等级?

因为「危险」是主观的,改写历史在本地分支上完全是常规操作——把三个临时提交压成一个、改掉写错的提交信息,都靠改写历史完成。真正需要拦住的只有一种情况:改写了别人已经拉走的历史。所以本页把「改本地」和「改远程」分开标注,本地随便改,远程改之前要想清楚。

git revert 和 git reset 到底差在哪?

revert 生成一个新提交,它的改动内容是被撤销提交的「反向补丁」,原提交仍然留在历史里可达,所以不需要强推,协作者无感。reset 是把分支引用往回搬,原提交从分支上脱离,远端要同步就必须强推。一句话:已经推送出去了就用 revert,只在本地没推过才用 reset。

--force 和 --force-with-lease 有什么区别?

前者无条件把远端分支指到你的本地位置,如果同事在你上次拉取后又推了新提交,那些提交会被直接丢掉。后者在推送前先比对远端引用是否仍等于你上次看到的值,只要别人动过就拒绝推送。团队协作里应该固定用 --force-with-lease,把裸 --force 当成例外。

误执行 git reset --hard 之后还能救回来吗?

分两种情况。丢掉的是已经提交过的内容:用 git reflog 找到搬运前的 HEAD 位置,再 git reset --hard <那个 ID> 就能回来,默认 90 天内有效。丢掉的是从未提交的工作区改动:找不回来,那部分内容从来没进过对象库。这也是本页把 reset --hard 备注成高危的原因。

git commit --amend 也算改写历史吗?

算,而且是连锁的。amend 会新建一个提交对象替换最近一次提交,新对象的 ID 与旧对象不同;如果这条分支已经有后代提交,所有后代的 ID 也会跟着变。所以 amend 之后如果之前推送过,就必须强推,而强推的后果按上一条处理。提交还没推送时 amend 是安全的。

git bisect 会不会影响我的工作区?

不会改写历史,但它确实会切换 HEAD,把工作区切到中间某个提交上,所以开始之前应该先清干净工作区。查完务必执行 git bisect reset 回到原来的分支,否则仓库会停在游离 HEAD 状态,看起来像「提交不见了」——其实只是 HEAD 没有指向任何分支。

git clean -fd 删掉的文件能找回吗?

不能。它删除的是没有被 Git 跟踪的文件和目录,不经过回收站,也不在 reflog 或对象库里留记录。习惯做法是先跑 git clean -n 预览一遍要删什么,确认没有 .env、日志、本地数据这类只存在于工作区的东西,再执行 -fd。

表格里的 git gc 为什么也被标成改写?

因为它不动提交,但会动对象库。默认策略下不可达对象要在 gc.reflogExpireUnreachable 规定的 30 天后才可能被回收,而 --prune=now 是要求立刻清理。清理之后,那些只靠 reflog 兜底的「误删提交」就真的没了。所以它是本页唯一的「改写仓库存储」条目,用之前先确认不再需要那些悬空对象。

相关工具