ToolkitX
知识库工具箱

Git 暂存与恢复

stash, reset, revert, checkout

20min·进阶

01. stash——临时存起来

你正在开发一个功能,代码改了一半还没法 commit,突然要切到另一个分支修紧急 bug——stash 就是干这事的。它把你的改动暂存起来,让工作区变干净,之后随时可以恢复。 git stash——把工作区和暂存区的修改存进 stash 栈,工作区恢复干净。 git stash pop——把最近 stash 的改动恢复到工作区并删除这个 stash。 git stash apply——恢复但不删除 stash(可以反复恢复)。 git stash list——看当前有哪些 stash。 默认 stash 不会保存未跟踪的文件(新文件),加 -u 参数(或 --include-untracked)才保存。加 -a 连被 .gitignore 忽略的文件也保存。
bash
# 临时保存改动
git stash                   # 快速保存
git stash -u                # 包含未跟踪的文件
git stash -m "WIP: login feature"  # 起个名

# 查看 stash 列表
git stash list

# 恢复
git stash pop               # 恢复最近一个并删除
git stash apply              # 恢复但不删除
git stash pop stash@{2}      # 恢复指定的 stash

# 删除
git stash drop stash@{0}    # 删一个
git stash clear              # 删所有
stash 是一个栈——后 stash 的在上面。用 git stash list 看编号,stash@{0} 是最新的,stash@{1} 是次新的。

02. reset——撤销 commit

reset 的三个模式区别很大: --soft——只撤销 commit 本身,改动回到暂存区(就像没 commit 过)。适合「刚才那个 commit 太快了,改一下 message 再 commit」。 --mixed(默认)——撤销 commit + 取消暂存区,改动回到工作区。适合「commit 了不该 commit 的东西,要拆成多个 commit」。 --hard——撤销一切,回到指定状态。工作区和暂存区的改动全丢。适合「这个方向完全错了,从头来过」。但如果你要撤销的 commit 已经 push 了,--hard 之后 push 需要 --force。 git reflog 可以救回误操作的 reset,因为 Git 对 HEAD 的每次移动都有记录(默认保留 90 天)。
bash
# 三种 reset
git reset --soft HEAD~1     # commit 撤回,改动在暂存区
git reset --mixed HEAD~1    # commit 撤回,改动在工作区(未暂存)
git reset --hard HEAD~1     # 全部撤回,改动丢失

# 回到某个特定的 commit
git reset --hard abc1234

# 救回误 reset 的 commit
git reflog                  # 找到被丢掉的 commit hash
git reset --hard abc1234    # 回到那个状态
git reset --hard 之后 push 需要 git push --force。如果别人已经 pull 了你之前的 commit,force push 可能导致他们的仓库出问题。

03. revert——安全的撤销

reset 会改写历史,在共享分支上很危险。revert 是安全的选择——它创建一个新的 commit,这个 commit 的内容是某个旧 commit 的逆操作。历史不改写,只是往前加一个回退 commit。 git revert abc1234 创建一个新 commit,把 abc1234 那次提交的改动全部还原。跟 reset 不同,revert 之后历史上有两条记录:原来的 commit 和还原的 commit。 多人协作的分支上,永远用 revert 而不是 reset。如果你已经 push 了错误 commit,reset + force push 会让别人的仓库出问题,revert 不会。
bash
# 还原某个 commit
git revert abc1234

# 还原但不自动提交(可以先检查或合并)
git revert --no-commit abc1234

# 还原一个 merge commit(需要指定保留哪个父提交)
git revert -m 1 abc1234

# 还原最近几个 commit(从旧到新)
git revert HEAD~3..HEAD
在共享分支上(main、develop)出了事用 revert。在自己的 feature 分支上用 reset 没问题,反正只有你一个人在上面。

04. checkout——切换与探索

git checkout 是个多功能命令,能切分支、切 commit、恢复文件(新版 Git 建议用 git switch 切分支,用 git restore 恢复文件,功能拆分更清晰)。 切分支:git checkout feature-branch。切到某个历史 commit:git checkout abc1234(进入 detached HEAD 状态——不挂任何分支上,如果在这里 commit 了容易丢)。 切文件:git checkout -- file.txt 恢复工作区文件到最后一次 commit 的状态。这个操作不可逆,改动的文件直接丢了。 创建并切换到新分支:git checkout -b new-feature。从某个特定 commit 分叉出来:git checkout -b hotfix abc1234。
bash
# 切换分支
git switch main                     # Git 2.23+ 推荐
# git checkout main                 # 传统写法

# 创建并切换新分支
git switch -c new-feature           # 推荐
git checkout -b new-feature         # 传统写法

# 恢复文件
git restore file.txt                # 推荐
git checkout -- file.txt            # 传统写法

# 切换到某个历史 commit
git checkout abc1234                # detached HEAD
git switch -c repair-branch abc1234 # 基于旧 commit 建新分支
Git 2.23+ 引入了 git switch 和 git restore,把 checkout 的多重功能拆分清楚。新手从这两个命令入手更不容易搞混。

05. clean——清除未跟踪的文件

git clean 清除工作区里没被 Git 跟踪的文件和目录(比如编译产物、临时文件)。这个操作非常危险——删掉的文件没经过 Git 管理,没法恢复。 git clean -n——先看看哪些文件会被删(dry run 不真删)。 git clean -f——删除未跟踪的文件(不包括目录)。 git clean -fd——删除未跟踪的文件和目录。 git clean -fdx——连 .gitignore 里忽略的文件也清理。 常见用法:配合 git reset --hard 完全重置工作区——git reset --hard && git clean -fd。把项目恢复到 clone 下来的干净状态。
bash
# 先看看会删什么
git clean -n

# 删除未跟踪的文件
git clean -f

# 删除未跟踪的文件+目录
git clean -fd

# 连 .gitignore 里的文件也删
git clean -fdx

# 完全重置(慎用)
git reset --hard && git clean -fd
git clean -fd 删除的东西永远找不回来。执行前一定先用 -n 看看要删什么,确认没有重要文件。

知识测验

1/5正确 0

git stash pop 和 git stash apply 的区别?

下一节

Git 远程协作

下一节