丢弃工作区里对该文件的修改,恢复成暂存区的样子。
旧版写法是 git checkout -- <file>。
只适用于还没 push 的提交。已经推上去的再 amend,就得 force push,会影响已经拉过这次提交的人。
这三条都会真删掉没提交过的改动,Git 从没记录过它们,reflog 也找不回来。不确定就先 git stash 收起来。
别用 reset + force push。别人已经基于那个提交继续干活了,历史被改写会让所有人的本地分支分叉。
只能救回「提交过」的东西。从没 commit 过的改动不在 reflog 里。
.gitignore 只对「从未被跟踪过」的文件生效,已经进过版本库的必须手动移出索引。
共享分支(main / develop)上别这么干。个人特性分支合并前整理是常规操作。
推上去的那一刻就要当成已泄露。GitHub 的 fork、CI 日志、别人的本地克隆都可能留着副本,清历史不等于没泄露过。
丢弃工作区里对该文件的修改,恢复成暂存区的样子。
旧版写法是 git checkout -- <file>。
丢弃当前目录及子目录下所有未暂存的修改。
把文件撤出暂存区,改动仍留在工作区。
旧版写法是 git reset HEAD <file>。
把某个文件恢复成两次提交之前的内容。
旧写法,等价于 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 checkout -b <new>。
从指定提交、分支或标签新建分支。
切回上一个待过的分支。
切到某个具体提交(游离 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 也没记录的情况下,这是最后一根稻草。
注册后台定时维护任务,自动做 gc、预取等。
给同一个仓库再开一个工作目录,检出另一个分支。
临时要看另一个分支又不想 stash 手上的活,比 clone 一份省空间。
列出所有工作目录。
移除一个工作目录。
只检出仓库里的部分目录,适合单体大仓库。
从整个历史里彻底删除某个文件或目录。
需要单独安装 git-filter-repo。会重写所有提交哈希,协作者必须重新克隆。
把当前版本打包成 zip,不含 .git 目录。
添加一个子模块。
初始化并拉取所有子模块(含嵌套)。
克隆时忘了 --recurse-submodules,补这一条。
把子模块更新到它自己远端分支的最新提交。
对每个子模块执行同一条命令。
卸载子模块的工作目录。
在本机启用 Git LFS。
把某类大文件交给 LFS 管理。
只对之后新增的文件生效,已经在历史里的大文件还得用 filter-repo 清。
列出当前由 LFS 管理的文件。
在浏览器里拖着分支图练 merge / rebase / cherry-pick,每一步都画出提交树。想真正搞懂变基,玩一遍比看十篇文章管用。
Git 官方推荐的那本书,中文全译在线可读。要理解「Git 内部到底存了什么」,看第十章。
每个命令的全部参数。本页给的是常用写法,遇到冷门选项回这里查。
按「我搞砸了什么」组织的急救清单,和本页顶部的场景导航互补。
Git 命令速查把 152 条常用命令按 12 类整理成中文对照表,点命令即复制。和普通 cheatsheet 不同的地方有两处:顶部按「我想干什么」组织了一份场景导航,以及每条会丢东西的命令都单独标了出来。
红色的 ⚠ 会丢改动:执行后未提交的内容会消失,reflog 也找不回来(reset --hard、clean -fd、restore、stash clear)。
橙色的 改写历史:不会丢你自己的东西,但会改变已有提交的哈希(commit --amend、rebase、push --force-with-lease)。这类命令在个人分支上是常规操作,在共享分支上会给所有协作者制造麻烦。
没有标记的命令都是可逆的,放心敲。
大部分 Git 事故的模式都一样——手上有没提交的改动,想试个命令,结果改动没了。git stash -u 的成本是两秒钟,而且它只在本地,不会被推出去。不确定某条命令的后果时,先收起来再说。
页面底部收了四个资源,其中 Learn Git Branching 值得单独说一句:它在浏览器里把提交树画出来,你敲一条命令就能看到分支图怎么变。merge 和 rebase 的区别,看一遍动画比读十篇文章都清楚。 有中文界面,一小时能过完主线关卡。
看这个提交推没推出去。 没推过用 reset:它直接把分支指针往回移,历史变干净,反正只有你自己有这段历史。已经推过用 revert:它新建一个内容相反的提交来抵消,原提交仍在历史里,别人拉下来不会冲突。已推送的提交用 reset 再 force push,会让所有基于它继续工作的人本地分支分叉,是协作里最招人恨的操作之一。
区别只在于「往回退多少层」。 Git 有三处状态:提交历史、暂存区、工作区。--soft 只退提交历史,改动留在暂存区(接着 commit 就行);--mixed(默认)退提交历史和暂存区,改动留在工作区(要重新 add);--hard 三处全退,改动直接消失。前两个随时可以反悔,--hard 丢掉的未提交内容 reflog 也救不回来——它从来没被 Git 记录过。
共享分支用 merge,个人分支整理用 rebase。 rebase 是把你的提交摘下来重新贴到新基上,每个提交都会得到新的哈希——这等于改写历史。在只有你一个人的特性分支上这么做没问题,能让合并进主干时的历史是一条直线;在 main 或别人也在用的分支上这么做,别人的本地分支会立刻和远端对不上。一条稳妥的默认规则:已经推给别人看过的提交,就别再改写它。
--force 是无条件覆盖,--force-with-lease 会先确认远端没有你不知道的新提交。 场景是这样:你 rebase 完准备强推,但在此期间同事往同一分支推了东西。--force 会把同事的提交直接抹掉,而且不会有任何提示;--force-with-lease 发现远端比你上次 fetch 时更新了,会拒绝推送让你先去看看。代价为零,收益是避免一次很难解释的事故,没有理由不换。
提交过的能,没提交过的不能。 git reflog 记录了 HEAD 的每一次移动,包括被 reset 掉的位置,默认保留 90 天,找到哈希后 git reset --hard <hash> 就能回去。但它记的是「提交」这个动作,从没 commit 过的工作区改动不在其中——git reset --hard、git checkout -- <file>、git clean -fd 丢掉的未提交内容是真的没了。所以不确定的时候,先 git stash 而不是先 reset。
因为 checkout 一个命令干了两件不相干的事。 它既能切分支,又能丢弃文件修改——后者会不可逆地删掉你的改动,却和「切分支」共用一个动词,手一滑就是事故。Git 2.23 把它拆成了 switch(只管分支)和 restore(只管文件内容),语义清楚得多。checkout 出于兼容保留着,新代码建议用拆开的两个。本页两种写法都收了,看到老教程也能对上。