git undo keep changes——给每一种害怕的拼写准备的 FAQ
给「撤销我的提交但留下改动」的每一种拼写准备的 FAQ——一条命令答全家,而唯一会删掉工作的那个参数从来都不需要。
■ space · 散文车道 · 问答
一条命令答全家——git reset --soft HEAD~1 把分支标签往回退一个提交,并把你的工作整个以已暂存状态还给你。
undo、cancel、revert 并不是 git 的三个功能。其中两个是同一个 reset 换了口音;revert 是异类,因为它是往历史上追加,而不是倒带。
已经推过了?revert,永远别改写——谁拉过谁手里就握着旧提交。
git reset --soft HEAD~1 是每一种 keep-changes 拼写的答案:分支标签往回退一个提交,你的工作以已暂存状态回来,随时可以重新提交。提交已经推了?改用 git revert HEAD——它追加一个反向提交,什么也不改写。动词对照表
人们敲的每个词、git 实际做的事、以及工作能不能活下来:
| 人们敲的词 | git 实际做的 | 保工作吗? | 命令 |
|---|---|---|---|
| "undo" | 分支标签往回退一个提交;改动以已暂存状态回来 | 能——在暂存区里 | git reset --soft HEAD~1 |
| "cancel" | 默认 reset:标签回退,改动回来但未暂存 | 能——在工作区里 | git reset HEAD~1 |
| "revert" | 追加一个撤销旧提交的新提交;历史从不改写 | 能——以反向 diff 的形式 | git revert HEAD |
| "unstage" | 路径移出暂存区;文件内容分毫未动 | 能——什么都没离开工作区 | git restore --staged file.txt |
| "先搁置" | 工作被打包放上架子,随时可以取回 | 能——在 stash 栈上 | git stash push -m "wip" |
唯一要紧的决定:有人拉过它吗?
没有——它还是你的:git reset --soft HEAD~1。标签回退,你的工作以已暂存状态回来。有——有人拉过了:git revert HEAD。历史上多一个反向提交;谁拉到手的东西都不会被改写。无论哪种,工作都活下来——选的只是谁的这份历史保持为真。至于吓到你的那个 --hard:它是全家族唯一会删工作的成员,而要留改动从来用不着它。你找到这一页,恰恰就是为了永远不必敲它。
--soft 到底留下什么?
全部,已暂存。提交离开分支,diff 原封不动地回到暂存区,git status 显示 "changes to be committed"。修好你本来想修的再重新提交——或者先并进更多工作。它离 --amend 之差一步:amend 原地改写最后一个提交;soft 是撤销它,然后等你。
amend 算撤销吗?
算一个小撤销,只对最后一个提交。git commit --amend --no-edit 把已暂存的改动折进你刚做的提交——适合补漏掉的文件。推过提交那一节的原则仍然适用:amend 已推送的提交就是改写历史,所以在共享分支上,用 revert。
什么时候 --hard 才真是正确答案?
当你想让工作区精确等于某个提交,而且你已经看清楚会丢掉什么。git reset --hard HEAD~1 一并丢弃提交和未提交的修改——git 不会问第二遍。安全仪式只有一行:先 git branch backup-before-reset;分支是免费的。如果已经跑过了,git reflog 还记得 HEAD 大约 90 天内去过哪里——git reset --hard HEAD@{1} 能走回来。
怎么只丢掉一个文件里的修改?
git restore file.txt——老 git checkout -- file 的现代拼法。它只丢弃这一个路径在工作区的修改,别的一个不碰:不挪分支,不动暂存。想让文件回到某个具体提交:git restore --source <sha> file.txt。
stash 在这家族里排第几?
stash 是唯一把工作装盒上架、而不是挪动位置的动词。git stash push -m "wip" 把你的修改搁上架子,交给你一棵干净的工作树;git stash pop 取回来。任务做到一半要换分支时它是对的工具——而且它接受 pathspec,可以只搁置一个文件,其他照旧是脏的。
我已经敲错命令了。工作没了吗?
几乎从来不会。reset 挪的是标签;revert 加的是提交;谁都不删对象。git reflog 列出 HEAD 约 90 天内去过的每一处——找到错手之前那条记录,git reset --hard HEAD@{n} 就送你回到那里。真正救不回来的集合很窄:被 git clean -f 删掉的未跟踪文件,以及过了时钟期限的 reflog 记录。
动词家族到此为止 · 什么都没下载 · 全程本地