⭐ 觉得好用?收藏备用,下次直接打开 ✨ 开通会员去广告 · 更多权益即将开放 ☕ 支持作者
152 条命令按 12 类整理,全部带中文释义,点命令即复制。会丢东西的命令标了红—— 红色表示未提交的改动会真的消失,橙色表示改写了已有提交(推送过的要 force)。 不确定该敲哪条,先看下面的「我想…」。

🚑 我想…

刚提交完,发现提交信息写错了
  1. 打开编辑器改信息,改完存盘即可

只适用于还没 push 的提交。已经推上去的再 amend,就得 force push,会影响已经拉过这次提交的人。

提交了但还没 push,想撤销这次提交、改动留着
  1. 撤销提交,改动回到暂存区(改完接着 commit)
  2. 撤销提交,改动回到工作区(还要重新 add)
文件改乱了,想回到上次提交的样子
  1. 只丢弃某个文件的工作区修改
  2. 丢弃当前目录下所有工作区修改
  3. 工作区 + 暂存区一起清空,回到上次提交

这三条都会真删掉没提交过的改动,Git 从没记录过它们,reflog 也找不回来。不确定就先 git stash 收起来。

已经推到远端的提交要撤掉
  1. 生成一个反向提交,历史不变,直接 push 即可

别用 reset + force push。别人已经基于那个提交继续干活了,历史被改写会让所有人的本地分支分叉。

刚才 reset --hard 了,提交还能找回来吗
  1. 列出 HEAD 走过的每一步,找到误操作之前那行的哈希
  2. 回到那个时刻

只能救回「提交过」的东西。从没 commit 过的改动不在 reflog 里。

在错的分支上写了代码,还没提交
  1. 把改动(含未跟踪文件)收起来
  2. 切过去
  3. 把改动放回来
在错的分支上已经提交了
  1. 先记下那次提交的哈希
  2. 切到该去的分支
  3. 把提交搬过来
  4. 切回错的分支
  5. 把错放的那次提交去掉
.gitignore 写了,但文件还在被跟踪
  1. 清空索引,本地文件不动
  2. 按新的 .gitignore 重新建立索引
  3. 提交这次清理

.gitignore 只对「从未被跟踪过」的文件生效,已经进过版本库的必须手动移出索引。

想把一堆零碎提交合成一个
  1. 把要合并的那几行 pick 改成 squash(或 s)
  2. 已推送过的分支这样推回去

共享分支(main / develop)上别这么干。个人特性分支合并前整理是常规操作。

不小心把密码 / 密钥提交上去了
  1. 改密码、吊销 token —— 这一步比清历史紧急得多
  2. 从整个历史里抹掉该文件(需另行安装 git-filter-repo)
  3. 重写后的历史推回远端,并通知所有协作者重新克隆

推上去的那一刻就要当成已泄露。GitHub 的 fork、CI 日志、别人的本地克隆都可能留着副本,清历史不等于没泄露过。

拉取时冲突了,想先放弃这次操作
  1. 正在 merge 时用
  2. 正在 rebase 时用
  3. 正在 cherry-pick 时用
想知道某一行代码是谁、什么时候改的
  1. 只看 40–60 行,逐行显示提交、作者、时间
  2. 反过来:找哪次提交引入或删掉了这段字符串

📖 命令表

分类
⚠ 会丢改动 需 Git 2.23+ 撤销与后悔 #

丢弃工作区里对该文件的修改,恢复成暂存区的样子。

旧版写法是 git checkout -- <file>。

⚠ 会丢改动 需 Git 2.23+ 撤销与后悔 #

丢弃当前目录及子目录下所有未暂存的修改。

需 Git 2.23+ 撤销与后悔 #

把文件撤出暂存区,改动仍留在工作区。

旧版写法是 git reset HEAD <file>。

需 Git 2.23+ 撤销与后悔 #

把某个文件恢复成两次提交之前的内容。

⚠ 会丢改动 撤销与后悔 #

旧写法,等价于 git restore <file>。

和切分支共用一个命令是 restore/switch 被拆出来的原因。

撤销与后悔 #

撤销最近一次提交,改动保留在暂存区。

「commit 完发现少提交了一个文件」最常用的一条。

撤销与后悔 #

撤销最近一次提交,改动退回工作区(--mixed 是默认行为)。

⚠ 会丢改动 撤销与后悔 #

撤销最近一次提交并丢弃全部改动。

提交本身能用 reflog 找回,但没提交过的改动找不回。

改写历史 撤销与后悔 #

修改最近一次提交的信息和内容(暂存区里的东西会一起并进去)。

改写历史 撤销与后悔 #

把暂存区补进上一次提交,保持原提交信息不变。

撤销与后悔 #

生成一个内容相反的新提交来抵消指定提交,历史不被改写。

已经推送出去的提交要撤销,用这个而不是 reset。

撤销与后悔 #

撤销一次合并提交,-m 1 表示保留第一个父提交(通常是主线)。

撤销与后悔 #

列出 HEAD 的所有移动记录,包括被 reset 掉的提交。

误操作之后的第一反应就该是它。默认保留 90 天。

⚠ 会丢改动 撤销与后悔 #

按 reflog 里查到的哈希,把分支拉回那个时刻。

撤销与后悔 #

把指定提交的改动复制一份到当前分支。

撤销与后悔 #

同上,但只放进暂存区不自动提交,方便改完再提交。

撤销与后悔 #

预演:列出会被 git clean 删掉的未跟踪文件,什么都不删。

执行 clean 之前先跑这条,成本为零。

⚠ 会丢改动 撤销与后悔 #

删除所有未跟踪的文件和目录。

⚠ 会丢改动 撤销与后悔 #

连 .gitignore 里忽略的一起删(node_modules、dist、.env 都会没)。

撤销与后悔 #

把文件移出版本库但保留在本地磁盘上。

补写 .gitignore 之后清理已被跟踪的文件用它。

改写历史 撤销与后悔 #

改写历史后推送。远端若有你没见过的新提交会拒绝推送。

任何时候都该用它代替 --force:--force 会直接覆盖掉别人刚推的提交。

暂存与提交 #

看当前有哪些改动、在哪个分支、和上游差几个提交。

暂存与提交 #

短格式输出,一行一个文件,顶部带分支信息。

暂存与提交 #

把指定文件的改动放进暂存区。

暂存与提交 #

暂存当前目录及以下的所有改动。

暂存与提交 #

暂存整个仓库的所有改动,包括删除的文件。

暂存与提交 #

交互式逐块挑选要暂存的内容。

把一次大改动拆成几个语义清晰的提交,靠的就是这条。

暂存与提交 #

把暂存区的内容提交。

暂存与提交 #

跳过 add 直接提交。

只对已跟踪的文件生效,新建的文件不会被带上。

暂存与提交 #

跳过 pre-commit / commit-msg 钩子。

钩子卡住紧急修复时才用,别成为习惯。

暂存与提交 #

工作区 与 暂存区 的差异(还没 add 的部分)。

暂存与提交 #

暂存区 与 上次提交 的差异(已 add、待提交的部分)。

暂存与提交 #

工作区 + 暂存区 与上次提交的全部差异。

暂存与提交 #

两个提交、分支或标签之间的差异。

暂存与提交 #

只看改了哪些文件、各增删多少行。

暂存与提交 #

按词而不是按行对比。

改中文文案、Markdown 文档时比默认的整行高亮清楚得多。

暂存与提交 #

检查是否引入了行尾空格、空白错误。

分支 #

列出本地分支,当前分支带 *。

分支 #

列出本地 + 远程跟踪分支。

分支 #

列出分支并显示各自的上游分支和领先/落后提交数。

需 Git 2.23+ 分支 #

切换到已有分支。

需 Git 2.23+ 分支 #

基于当前位置新建分支并切过去。

旧写法 git checkout -b <new>。

需 Git 2.23+ 分支 #

从指定提交、分支或标签新建分支。

需 Git 2.23+ 分支 #

切回上一个待过的分支。

需 Git 2.23+ 分支 #

切到某个具体提交(游离 HEAD 状态),用于临时查看历史版本。

分支 #

旧写法:新建并切换分支。

分支 #

删除已经合并过的本地分支。

改写历史 分支 #

强制删除本地分支,不检查是否已合并。

删错了用 git reflog 查到哈希还能重建。

分支 #

重命名当前分支。

分支 #

列出已经合并进当前分支的分支。

配合 xargs 批量清理已合并的特性分支。

分支 #

列出还没合并进当前分支的分支。

改写历史 分支 #

删除远程分支。

合并与变基 #

把指定分支合并进当前分支。

合并与变基 #

即使能快进也强制生成一个合并提交。

想在历史里保留「这是一条特性分支」的形状时用。

合并与变基 #

把整条分支的改动压成一份放进暂存区,由你提交成一个提交。

合并与变基 #

冲突处理到一半,放弃合并回到合并前。

改写历史 合并与变基 #

把当前分支独有的提交,逐个重放到 base 之上。

改写历史 合并与变基 #

交互式整理最近 5 个提交:压缩(squash)、改序、改信息、删除。

合并与变基 #

解决完当前冲突,继续变基。

合并与变基 #

跳过当前这个提交,继续变基。

合并与变基 #

放弃变基,回到开始之前的状态。

改写历史 合并与变基 #

把分支从旧基上摘下来接到新基上,用于分支开错了地方。

合并与变基 #

拉取远端更新并把本地提交变基到其上,不产生多余的合并提交。

合并与变基 #

冲突时直接采用当前分支这一版。

rebase 期间 ours/theirs 的含义是反的:ours 指的是被变基到的那个基。

合并与变基 #

冲突时直接采用对方分支那一版。

合并与变基 #

记住你解决过的冲突,下次遇到一模一样的冲突自动套用。

长期维护的分支反复 rebase 时省掉大量重复劳动。

合并与变基 #

调用配置好的图形化工具解决冲突。

远程 #

看远程仓库的名字和地址。

远程 #

添加一个远程仓库。

远程 #

修改远程地址。

HTTPS 换 SSH、换域名、换托管平台都用它。

远程 #

重命名远程仓库。

远程 #

清掉远端已删除分支在本地留下的跟踪引用。

远程 #

拉取远端最新数据,但不改动你的工作区和分支。

远程 #

拉取所有远程仓库,并顺手清理已删除的远程分支引用。

远程 #

拉取并合并(等于 fetch + merge)。

远程 #

把当前分支推送到其上游分支。

远程 #

推送并建立上游关联,之后直接 git push / git pull 即可。

远程 #

把本地标签一起推送到远端。

远程 #

不克隆就查看远端有哪些分支和标签。

验证仓库地址对不对、权限通不通,比 clone 快得多。

查看与检索 #

一行一提交的分支拓扑图,看清分支怎么分怎么合。

值得配个别名,比如 git lg。

查看与检索 #

只看最近 10 条提交。

查看与检索 #

按时间筛选提交。

查看与检索 #

只看某个人的提交。

查看与检索 #

找出哪次提交引入或删除了这段字符串(pickaxe 搜索)。

排查「这行诡异代码是哪来的」最快的路子。

查看与检索 #

同 -S,但按正则匹配 diff 内容。

查看与检索 #

看某个文件历次改动的完整 diff。

查看与检索 #

追溯文件历史,跨过重命名继续往前查。

查看与检索 #

逐行显示这一行是谁在哪次提交改的。

查看与检索 #

只对 10–20 行做 blame。

查看与检索 #

blame 时忽略空白变化并追踪代码搬迁。

避免把「格式化全文件」的那个人算成所有代码的作者。

查看与检索 #

看某次提交的完整信息和 diff。

查看与检索 #

看某次提交时该文件的全文内容。

查看与检索 #

按作者统计提交数量,从多到少排。

查看与检索 #

开始二分查找,定位是哪次提交引入了问题。

查看与检索 #

标记当前版本有问题 / 标记某个版本是好的,Git 自动二分。

查看与检索 #

结束二分查找,回到原来的分支。

查看与检索 #

在仓库里搜索。

只搜被跟踪的文件,比 grep -r 快且不会翻进 node_modules。

查看与检索 #

用最近的标签加偏移量描述当前提交,常用于生成版本号。

储藏 #

把工作区和暂存区的改动收起来,回到干净状态。

储藏 #

连未跟踪的新文件一起收起来。

不加 -u 的话,新建的文件会被留在原地,切分支后还在。

储藏 #

只收指定文件,并给这条储藏加个说明。

储藏 #

列出所有储藏。

储藏 #

取出最近一条储藏并从列表里删除。

储藏 #

取出指定储藏但保留在列表里(可以往多个分支各应用一次)。

储藏 #

看最近一条储藏里具体改了什么。

⚠ 会丢改动 储藏 #

删掉指定储藏。

⚠ 会丢改动 储藏 #

清空所有储藏。

储藏不在任何分支上,清掉之后基本找不回来。

储藏 #

基于当初储藏时的那个提交新建分支并应用储藏。

储藏放太久、代码已经跟不上主线时用这条,能避开冲突。

标签 #

列出所有标签。

标签 #

打一个轻量标签(只是个指针,不记录作者和时间)。

标签 #

打一个附注标签,带作者、日期和说明。

正式发版用附注标签,git describe 默认也只认它。

标签 #

给一个历史提交补打标签。

标签 #

按通配符筛选标签。

标签 #

删除本地标签。

标签 #

推送单个标签。

git push 默认不带标签,漏了这步远端就没有。

改写历史 标签 #

删除远程标签。

配置 #

设置全局提交者名字。

配置 #

设置全局提交者邮箱。

公司仓库和个人仓库邮箱不同时,在仓库内去掉 --global 单独设。

配置 #

新建仓库时的默认分支名。

配置 #

让中文文件名正常显示。

不设的话 git status 里中文文件名会显示成 \344\275\240 这样的八进制转义。

配置 #

提交时把 CRLF 转成 LF,检出时不转(macOS / Linux 推荐)。

Windows 上用 true。跨平台协作的换行符问题基本都出在这。

配置 #

pull 默认用变基而不是合并,历史更干净。

配置 #

给长命令起别名。

配置 #

列出所有生效的配置,并显示每一条来自哪个文件。

排查「我明明设了怎么不生效」就用它。

配置 #

用 VS Code 编辑提交信息。

配置 #

记住 HTTPS 凭证。

明文存在 ~/.git-credentials,公用机器上别开;macOS 用 osxkeychain 更合适。

创建与克隆 #

在当前目录初始化一个新仓库。

创建与克隆 #

初始化并指定初始分支名。

创建与克隆 #

克隆远程仓库。

创建与克隆 #

浅克隆,只拿最近一次提交。

大仓库只是想看看代码时能快一个数量级,但不能推送历史相关操作。

创建与克隆 #

只克隆指定的一个分支。

创建与克隆 #

部分克隆:先只要提交历史,文件内容按需拉取。

比浅克隆保留更多信息,适合需要查历史的巨型仓库。

创建与克隆 #

克隆时把子模块一起拉下来。

清理与维护 #

看仓库里的对象数量和实际占用空间。

清理与维护 #

垃圾回收:打包松散对象、清理过期引用。

清理与维护 #

深度重打包并立刻清理不可达对象。

会让 reflog 里的东西真正消失,跑之前先确认没有要找回的提交。

清理与维护 #

检查仓库完整性并找出悬空的提交和对象。

reflog 也没记录的情况下,这是最后一根稻草。

需 Git 2.30+ 清理与维护 #

注册后台定时维护任务,自动做 gc、预取等。

清理与维护 #

给同一个仓库再开一个工作目录,检出另一个分支。

临时要看另一个分支又不想 stash 手上的活,比 clone 一份省空间。

清理与维护 #

列出所有工作目录。

清理与维护 #

移除一个工作目录。

清理与维护 #

只检出仓库里的部分目录,适合单体大仓库。

⚠ 会丢改动 清理与维护 #

从整个历史里彻底删除某个文件或目录。

需要单独安装 git-filter-repo。会重写所有提交哈希,协作者必须重新克隆。

清理与维护 #

把当前版本打包成 zip,不含 .git 目录。

子模块 / LFS #

添加一个子模块。

子模块 / LFS #

初始化并拉取所有子模块(含嵌套)。

克隆时忘了 --recurse-submodules,补这一条。

子模块 / LFS #

把子模块更新到它自己远端分支的最新提交。

子模块 / LFS #

对每个子模块执行同一条命令。

子模块 / LFS #

卸载子模块的工作目录。

子模块 / LFS #

在本机启用 Git LFS。

子模块 / LFS #

把某类大文件交给 LFS 管理。

只对之后新增的文件生效,已经在历史里的大文件还得用 filter-repo 清。

子模块 / LFS #

列出当前由 LFS 管理的文件。

🎓 想系统学,而不只是查

Git 命令速查把 152 条常用命令按 12 类整理成中文对照表,点命令即复制。和普通 cheatsheet 不同的地方有两处:顶部按「我想干什么」组织了一份场景导航,以及每条会丢东西的命令都单独标了出来。

危险标记的口径

红色的 ⚠ 会丢改动:执行后未提交的内容会消失,reflog 也找不回来(reset --hardclean -fdrestorestash clear)。

橙色的 改写历史:不会丢你自己的东西,但会改变已有提交的哈希(commit --amendrebasepush --force-with-lease)。这类命令在个人分支上是常规操作,在共享分支上会给所有协作者制造麻烦。

没有标记的命令都是可逆的,放心敲。

一条建议:先 stash,再动手

大部分 Git 事故的模式都一样——手上有没提交的改动,想试个命令,结果改动没了。git stash -u 的成本是两秒钟,而且它只在本地,不会被推出去。不确定某条命令的后果时,先收起来再说。

想系统学,而不只是查

页面底部收了四个资源,其中 Learn Git Branching 值得单独说一句:它在浏览器里把提交树画出来,你敲一条命令就能看到分支图怎么变。merge 和 rebase 的区别,看一遍动画比读十篇文章都清楚。 有中文界面,一小时能过完主线关卡。

📍使用场景

  • 刚把事情搞砸,需要一条正确的命令顶部「我想…」按真实处境组织:提交信息写错了、推错分支了、reset --hard 之后想找回来,点开就是分步命令。
  • 记不住参数细节时的日常查阅reset 的三种模式、stash 的各种子命令、rebase 冲突后的三个出口,搜一下比翻 man page 快。
  • 给团队新人一份中文对照每条命令都有中文释义和适用场景,危险命令单独标出,比直接丢一份英文 cheatsheet 更省事。
  • 上手前先弄清哪些命令会丢东西勾上「只看会丢东西的命令」,19 条一次看完,知道哪些敲之前该先 stash。

常见问题

撤销提交到底该用 reset 还是 revert?

看这个提交推没推出去。 没推过用 reset:它直接把分支指针往回移,历史变干净,反正只有你自己有这段历史。已经推过用 revert:它新建一个内容相反的提交来抵消,原提交仍在历史里,别人拉下来不会冲突。已推送的提交用 reset 再 force push,会让所有基于它继续工作的人本地分支分叉,是协作里最招人恨的操作之一。

reset 的 --soft、--mixed、--hard 有什么区别?

区别只在于「往回退多少层」。 Git 有三处状态:提交历史、暂存区、工作区。--soft 只退提交历史,改动留在暂存区(接着 commit 就行);--mixed(默认)退提交历史和暂存区,改动留在工作区(要重新 add);--hard 三处全退,改动直接消失。前两个随时可以反悔,--hard 丢掉的未提交内容 reflog 也救不回来——它从来没被 Git 记录过。

rebase 和 merge 该用哪个?

共享分支用 merge,个人分支整理用 rebase。 rebase 是把你的提交摘下来重新贴到新基上,每个提交都会得到新的哈希——这等于改写历史。在只有你一个人的特性分支上这么做没问题,能让合并进主干时的历史是一条直线;在 main 或别人也在用的分支上这么做,别人的本地分支会立刻和远端对不上。一条稳妥的默认规则:已经推给别人看过的提交,就别再改写它。

--force 和 --force-with-lease 差在哪?为什么推荐后者?

--force 是无条件覆盖,--force-with-lease 会先确认远端没有你不知道的新提交。 场景是这样:你 rebase 完准备强推,但在此期间同事往同一分支推了东西。--force 会把同事的提交直接抹掉,而且不会有任何提示;--force-with-lease 发现远端比你上次 fetch 时更新了,会拒绝推送让你先去看看。代价为零,收益是避免一次很难解释的事故,没有理由不换。

误操作之后,提交还能找回来吗?

提交过的能,没提交过的不能。 git reflog 记录了 HEAD 的每一次移动,包括被 reset 掉的位置,默认保留 90 天,找到哈希后 git reset --hard <hash> 就能回去。但它记的是「提交」这个动作,从没 commit 过的工作区改动不在其中——git reset --hardgit checkout -- <file>git clean -fd 丢掉的未提交内容是真的没了。所以不确定的时候,先 git stash 而不是先 reset。

为什么有了 checkout,还要有 switch 和 restore?

因为 checkout 一个命令干了两件不相干的事。 它既能切分支,又能丢弃文件修改——后者会不可逆地删掉你的改动,却和「切分支」共用一个动词,手一滑就是事故。Git 2.23 把它拆成了 switch(只管分支)和 restore(只管文件内容),语义清楚得多。checkout 出于兼容保留着,新代码建议用拆开的两个。本页两种写法都收了,看到老教程也能对上。