前言
在 Git 日常开发中,我们经常遇到这样的场景:正在开发一个功能,修改了一堆文件,突然需要切换到其他分支修复紧急 bug,但当前修改又不想提交。这时候 git stash 就派上用场了。本文将深入讲解 Git Stash 的原理、用法和最佳实践。
一、什么是 Git Stash?
1.1 核心概念
git stash 是 Git 提供的一个**栈(Stack)**数据结构,用于临时保存工作区和暂存区的修改,让工作目录恢复到干净状态(HEAD 状态)。
1.2 三个工作区域回顾
text
工作区 (Working Directory) ← 你实际修改文件的地方
↓ git add
暂存区 (Staging Area/Index) ← 准备提交的文件
↓ git commit
版本库 (Repository/HEAD) ← 已提交的历史版本
1.3 Stash 的本质
Stash 本质上是一个后进先出(LIFO)的栈结构:
text
入栈 push ↓ ↑ 出栈 pop
┌─────────┐
│ stash③ │ ← stash@{0} (最新)
├─────────┤
│ stash② │ ← stash@{1}
├─────────┤
│ stash① │ ← stash@{2} (最旧)
└─────────┘
二、基础命令详解
2.1 git stash - 保存当前修改
bash
# 最基础的用法:保存所有已跟踪文件的修改
git stash
# 等价于
git stash push
执行效果:
-
✅ 保存暂存区(staged)的修改
-
✅ 保存工作区(unstaged)的修改
-
❌ 不保存未跟踪的新文件
-
❌ 不保存被 .gitignore 忽略的文件
-
工作目录恢复到 HEAD 状态(干净状态)
示例:
bash
# 初始状态
$ echo "new feature" >> app.js
$ git add app.js
$ echo "bug fix" >> utils.js
$ git status
Changes to be committed:
modified: app.js # 暂存区
Changes not staged:
modified: utils.js # 工作区
# 执行 stash
$ git stash
Saved working directory and index state WIP on main: abc1234 last commit
$ git status
nothing to commit, working tree clean # 完全干净!
2.2 git stash pop - 恢复并删除
bash
# 恢复栈顶的 stash 并删除该记录
git stash pop
# 恢复指定的 stash
git stash pop stash@{2}
特点:
-
恢复 stash 中的修改到工作区
-
自动删除已恢复的 stash 记录
-
如果冲突,修改会保留但 stash 不删除
与 apply 的区别:
bash
# pop = apply + drop
git stash pop # 恢复 + 删除
git stash apply # 只恢复,不删除
git stash drop # 只删除,不恢复
2.3 git stash apply - 恢复但不删除
bash
# 恢复栈顶的 stash,stash 记录保留
git stash apply
# 恢复指定的 stash
git stash apply stash@{1}
使用场景:
-
想把同一个 stash 应用到多个分支
-
不确定恢复后是否会有问题,先试试看
2.4 git stash drop - 删除 stash
bash
# 删除栈顶的 stash
git stash drop
# 删除指定的 stash
git stash drop stash@{3}
# 清空所有 stash
git stash clear
三、进阶命令详解
3.1 保存未跟踪的文件
bash
# -u 或 --include-untracked:包括未跟踪的新文件
git stash -u
# 示例
$ touch newfile.txt # 新建文件,未 git add
$ git stash -u # 会保存 newfile.txt
3.2 保存所有文件(包括被忽略的)
bash
# -a 或 --all:保存所有文件,包括 .gitignore 中忽略的
git stash -a
3.3 添加描述信息
bash
# 带描述的 stash,方便后续查找
git stash push -m "修复登录页面样式"
# 或者
git stash save "修复登录页面样式" # 旧语法
# 查看时更清晰
$ git stash list
stash@{0}: On main: 修复登录页面样式
stash@{1}: WIP on main: abc1234 某个提交
3.4 只保存部分文件
bash
# 交互式选择要 stash 的代码块
git stash -p
# 只 stash 特定文件
git stash push app.js utils.js
# 只 stash 特定文件(带描述)
git stash push -m "只保存前端文件" app.js utils.js
3.5 保持暂存区不变
bash
# --keep-index:stash 工作区的修改,但保留暂存区
git stash --keep-index
# 使用场景:想测试暂存区的代码,临时去掉工作区的干扰
3.6 从 stash 创建分支
bash
# 将 stash 恢复到一个新分支
git stash branch <branch-name>
# 等价于:
git checkout -b <branch-name>
git stash pop
使用场景:stash 的代码与当前分支冲突严重,需要在独立分支解决。
四、查看与检查
4.1 查看 stash 列表
bash
# 列出所有 stash
git stash list
# 输出示例
stash@{0}: WIP on main: abc1234 fix: 修复登录bug
stash@{1}: On develop: def5678 feat: 新功能开发中
4.2 查看 stash 内容
bash
# 查看 stash 的简要信息
git stash show
# 查看 stash 的详细 diff
git stash show -p
# 查看指定 stash
git stash show stash@{1}
git stash show -p stash@{1}
4.3 查看 stash 中的文件名
bash
# 只显示文件名
git stash show --name-only
五、完整使用场景
场景1:开发中临时切换分支
bash
# 1. 正在开发功能,修改了很多文件
$ git status -s
M app.js
M utils.js
M style.css
# 2. 需要紧急修复 bug
$ git stash push -m "新功能开发-登录模块"
# 3. 切换到 bug 修复分支
$ git checkout hotfix
# 4. 修复 bug 并提交
$ git add .
$ git commit -m "fix: 紧急修复支付bug"
# 5. 切回开发分支
$ git checkout feature
# 6. 恢复之前的工作
$ git stash pop
场景2:多个 stash 的管理
bash
# 保存第一个任务
$ git stash push -m "任务1:登录页面"
# 保存第二个任务
$ git stash push -m "任务2:数据列表"
# 保存第三个任务
$ git stash push -m "任务3:图表优化"
# 查看所有 stash
$ git stash list
stash@{0}: On feature: 任务3:图表优化
stash@{1}: On feature: 任务2:数据列表
stash@{2}: On feature: 任务1:登录页面
# 先恢复任务2(不是最新的任务3)
$ git stash pop stash@{1}
场景3:部分文件暂存
bash
# 修改了3个文件,只想 stash 其中2个
$ git stash push app.js utils.js -m "暂存app和utils"
$ git status
Changes not staged:
modified: style.css # 这个文件还在工作区
场景4:stash 冲突处理
bash
# 恢复 stash 时发生冲突
$ git stash pop
Auto-merging app.js
CONFLICT (content): Merge conflict in app.js
# stash 没有被删除
# 解决冲突
$ vim app.js # 手动解决冲突标记
# 标记为已解决
$ git add app.js
# 手动删除 stash
$ git stash drop
# 或者放弃恢复
$ git reset --hard HEAD
$ git stash pop
场景5:将 stash 应用到不同分支
bash
# 在 feature 分支 stash
$ git stash
# 切换到 develop 分支
$ git checkout develop
# 应用 feature 分支的 stash
$ git stash apply # 用 apply 而不是 pop,保留 stash
# 如果还要应用到其他分支
$ git checkout release
$ git stash apply
六、最佳实践与技巧
6.1 命名规范
bash
# ✅ 好的实践:有意义的描述
git stash push -m "[WIP] 登录模块 - 表单验证未完成"
git stash push -m "[BUG] 修复支付流程中的空指针 - 等待后端接口"
# ❌ 不好的实践:没有描述
git stash
6.2 定期清理
bash
# 查看 stash 列表
git stash list
# 删除不用的 stash
git stash drop stash@{2}
git stash drop stash@{3}
# 或者一次性清空
git stash clear
# 💡 建议:stash 数量不要超过5个,太多了容易混乱
6.3 使用别名提高效率
bash
# 在 ~/.gitconfig 中添加
[alias]
ss = stash
sl = stash list
sp = stash pop
sa = stash apply
sd = stash drop
sc = stash clear
sw = stash show
# 使用
git ss -m "快速暂存"
git sl
git sp
6.4 stash 与 WIP 提交的选择
bash
# 什么时候用 stash?
✅ 临时切换分支(几分钟到几小时)
✅ 代码还没到可以提交的程度
✅ 需要快速保存现场
# 什么时候用 WIP 提交?
✅ 需要保存较长时间的修改
✅ 需要推送到远程备份
✅ 需要与他人共享进度
# WIP 提交示例
git add .
git commit -m "WIP: 新功能开发中"
# 回来后继续开发,最后用 rebase 合并
git reset HEAD~1 # 撤销 WIP 提交,保留修改
6.5 查看 stash 的完整差异
bash
# 查看 stash 相对于原始位置的完整 diff
git stash show -p stash@{0}
# 对比 stash 与当前工作区的差异
git diff stash@{0}
# 查看 stash 中某个文件的差异
git diff stash@{0} -- app.js
七、常见问题与解决
Q1: pop 时发生冲突怎么办?
bash
# pop 冲突后,stash 不会被删除
# 1. 手动解决冲突文件
# 2. git add <解决的文件>
# 3. git stash drop # 手动删除 stash
Q2: 不小心删除了 stash 能恢复吗?
bash
# 1. 找到被删除的 stash 的 commit hash
git fsck --unreachable | grep commit | cut -d ' ' -f3 | xargs git log --merges --no-walk --grep=WIP
# 2. 恢复指定的 stash
git stash apply <commit-hash>
Q3: stash 的文件在哪里?
bash
# stash 存储在 .git/refs/stash
cat .git/refs/stash
# 使用 git log 查看 stash 历史
git log --oneline --graph stash
Q4: 如何只 stash 暂存区或只 stash 工作区?
bash
# 只 stash 工作区(保留暂存区)
git stash --keep-index
# 只 stash 暂存区(保留工作区)
git stash --staged # Git 2.35+
# 旧版本需要两步:
git stash --keep-index # 先 stash 工作区
git stash # 再 stash 暂存区
git stash pop stash@{1} # 恢复工作区的修改
八、命令速查表
| 命令 | 功能 | 常用场景 |
|---|---|---|
git stash |
保存当前修改 | 临时切换任务 |
git stash -u |
包括未跟踪文件 | 有新建文件 |
git stash -m "描述" |
带描述的暂存 | 多个 stash 需要区分 |
git stash pop |
恢复并删除 | 恢复最新暂存 |
git stash apply |
恢复不删除 | 应用到多个分支 |
git stash list |
查看列表 | 检查所有暂存 |
git stash show |
查看内容 | 确认暂存内容 |
git stash drop |
删除指定 | 清理不用的暂存 |
git stash clear |
清空所有 | 大清理 |
git stash branch <name> |
创建分支并恢复 | 解决严重冲突 |
git stash -p |
交互式暂存 | 部分代码暂存 |
九、总结
git stash 是 Git 工作流中的"暂停键",掌握它能让你更灵活地管理开发过程:
-
理解栈结构:后进先出,pop 总是取最新的
-
命名很重要 :带
-m参数的描述能避免混乱 -
善用 apply:不确定时用 apply 而不是 pop
-
定期清理:避免 stash 列表堆积过多
-
WIP 提交是替代方案:需要长期保存或远程备份时使用
记住核心原则:stash 是临时性的,提交是永久性的。根据实际需求选择合适的工具,让版本控制更高效。
希望这篇指南能帮助你更好地理解和使用 Git Stash。如果在实践中有任何问题,欢迎交流讨论!