Git: Git暂存与多任务切换详解
暂存(Stash)是临时保存工作区修改的功能。当需要切换分支处理紧急任务,但又不想提交未完成的工作时,stash是最佳选择。理解stash的使用对多任务开发非常重要。
1. 暂存基础概念
(1) 什么是暂存
暂存是将工作区和暂存区的修改临时保存起来:
- 临时保存:保存未提交的修改
- 清理工作区:工作区恢复到干净状态
- 切换分支:可以自由切换分支
- 稍后恢复:随时恢复保存的修改
graph TB
A[工作区有修改] --> B[git stash]
B --> C[修改保存到stash]
C --> D[工作区变干净]
D --> E[切换分支工作]
E --> F[git stash pop]
F --> G[恢复修改]
style B fill:#fff3cd
style D fill:#d4edda
style G fill:#c3e6cb
(2) stash与commit的区别
| 特性 | stash | commit |
|---|---|---|
| 目的 | 临时保存 | 永久记录 |
| 历史 | 不在提交历史中 | 在提交历史中 |
| 分支 | 不属于任何分支 | 属于当前分支 |
| 推送 | 不会推送 | 会推送 |
| 适用场景 | 临时切换任务 | 完成功能开发 |
(3) stash的存储结构
stash是一个栈结构:
- stash@{0}:最新的暂存
- stash@{1}:次新的暂存
- stash@{n}:第n+1个暂存
2. 基本暂存操作
(1) 暂存当前修改
▶ 示例:暂存修改
BASH
# 查看当前修改
git status
# 输出:
# Changes not staged for commit:
# modified: src/auth.js
# modified: src/user.js
# 暂存当前修改
git stash
# 输出:
# Saved working directory and index state WIP on main: a1b2c3d feat: 添加功能
# 查看状态(工作区已干净)
git status
# 输出:
# On branch main
# nothing to commit, working tree clean
(2) 带描述的暂存
▶ 示例:添加描述
BASH
# 暂存并添加描述
git stash save "WIP: 用户登录功能"
# 或者使用push(新语法)
git stash push -m "WIP: 用户登录功能"
# 输出:
# Saved working directory and index state On main: WIP: 用户登录功能
# 查看暂存列表
git stash list
# 输出:
# stash@{0}: On main: WIP: 用户登录功能
(3) 暂存包括未跟踪文件
▶ 示例:包含未跟踪文件
BASH
# 创建新文件(未跟踪)
touch new-file.js
# 默认stash不会保存未跟踪文件
git stash
# 输出:
# Saved working directory and index state WIP on main: a1b2c3d
# new-file.js still untracked
# 使用-u选项包含未跟踪文件
git stash -u
# 或者
git stash --include-untracked
# 使用-a选项包含所有文件(包括忽略的文件)
git stash -a
3. 查看和管理暂存
(1) 查看暂存列表
▶ 示例:列出所有暂存
BASH
# 查看暂存列表
git stash list
# 输出:
# stash@{0}: On main: WIP: 用户登录功能
# stash@{1}: On feature: WIP: 购物车功能
# stash@{2}: On develop: 修复Bug
# 限制显示数量
git stash list -3
# 查看暂存详情
git stash show
# 输出:
# src/auth.js | 10 ++++++++++
# src/user.js | 5 ++---
# 2 files changed, 12 insertions(+), 3 deletions(-)
# 查看详细的差异
git stash show -p
# 查看特定暂存
git stash show stash@{1}
(2) 查看暂存内容
▶ 示例:查看暂存详情
BASH
# 查看最新暂存的完整差异
git stash show -p stash@{0}
# 输出:
# diff --git a/src/auth.js b/src/auth.js
# index abc1234..def5678 100644
# --- a/src/auth.js
# +++ b/src/auth.js
# @@ -10,6 +10,16 @@ function validate() {
# + // 新增验证逻辑
# + if (!token) {
# + return false;
# + }
# return true;
# }
# 查看暂存的统计信息
git stash show --stat
# 输出:
# src/auth.js | 10 ++++++++++
# src/user.js | 5 ++---
# 2 files changed, 12 insertions(+), 3 deletions(-)
(3) 删除暂存
▶ 示例:删除暂存
BASH
# 删除最新暂存
git stash drop
# 删除指定暂存
git stash drop stash@{2}
# 清空所有暂存
git stash clear
# 验证删除
git stash list
4. 应用暂存
(1) 应用并删除暂存(pop)
▶ 示例:应用暂存
BASH
# 应用最新暂存并删除
git stash pop
# 输出:
# On branch main
# Changes not staged for commit:
# modified: src/auth.js
# modified: src/user.js
# 应用指定暂存
git stash pop stash@{1}
# 如果有冲突
# CONFLICT (content): Merge conflict in src/auth.js
# The stash entry is kept in case you need it again.
# 解决冲突后,手动删除暂存
git stash drop
(2) 应用但保留暂存(apply)
▶ 示例:保留暂存
BASH
# 应用暂存但不删除
git stash apply
# 应用指定暂存
git stash apply stash@{1}
# 查看暂存列表(暂存还在)
git stash list
# 输出:
# stash@{0}: On main: WIP: 用户登录功能
# stash@{1}: On feature: WIP: 购物车功能
# apply适合多次应用同一暂存
(3) pop vs apply
graph TB
A[应用暂存] --> B{方式}
B -->|pop| C[应用并删除暂存]
B -->|apply| D[应用但保留暂存]
C --> E[一次性使用]
D --> F[可多次使用]
style C fill:#d4edda
style D fill:#fff3cd
使用pop的场景:
- 只需要应用一次
- 确定不再需要该暂存
使用apply的场景:
- 可能需要多次应用
- 想保留暂存作为备份
- 应用到多个分支
5. 暂存选项详解
(1) 常用暂存选项
| 选项 | 说明 | 使用场景 |
|---|---|---|
save |
保存暂存(旧语法) | 基本暂存 |
push |
保存暂存(新语法) | 推荐使用 |
-m |
添加描述 | 标识暂存内容 |
-u |
包含未跟踪文件 | 新文件也要暂存 |
-a |
包含所有文件 | 包括忽略的文件 |
-k |
保留暂存区 | 只暂存工作区 |
▶ 示例:使用暂存选项
BASH
# 只暂存已跟踪文件的修改
git stash
# 暂存包括未跟踪文件
git stash -u
# 暂存包括忽略的文件
git stash -a
# 只暂存工作区,保留暂存区
git stash -k
# 只暂存特定文件
git stash push src/auth.js
# 只暂存匹配模式的文件
git stash push '*.js'
# 保留暂存区索引
git stash --index
(2) 从暂存创建分支
▶ 示例:从暂存创建分支
BASH
# 从暂存创建新分支
git stash branch feature-from-stash
# 输出:
# Switched to a new branch 'feature-from-stash'
# On branch feature-from-stash
# Changes not staged for commit:
# modified: src/auth.js
# 这会创建新分支并应用暂存
# 适合将暂存的修改转移到新分支开发
6. 暂存工作流
(1) 紧急任务切换
▶ 示例:处理紧急Bug
BASH
# 正在开发功能A
echo "feature A code" > feature-a.js
git status
# 输出:
# Untracked files:
# feature-a.js
# 突然需要修复紧急Bug
# 暂存当前工作
git stash save "WIP: 功能A开发中"
# 切换到hotfix分支
git checkout -b hotfix-urgent-bug
# 修复Bug
echo "fix" > fix.js
git add fix.js
git commit -m "fix: 修复紧急Bug"
# 推送修复
git push origin hotfix-urgent-bug
# 切回原分支恢复工作
git checkout main
git stash pop
# 继续开发功能A
(2) 多任务并行开发
sequenceDiagram
participant 开发者
participant main分支
participant feature分支
participant stash栈
开发者->>main分支: 开发功能A
开发者->>stash栈: git stash保存A
开发者->>feature分支: 切换分支开发B
开发者->>stash栈: git stash保存B
开发者->>main分支: 切回main
开发者->>stash栈: git stash pop恢复A
▶ 示例:多任务切换
BASH
# 开发功能A
echo "feature A" > feature-a.js
git stash push -m "功能A"
# 切换开发功能B
git checkout -b feature-b
echo "feature B" > feature-b.js
git stash push -m "功能B"
# 查看暂存列表
git stash list
# 输出:
# stash@{0}: On feature-b: 功能B
# stash@{1}: On main: 功能A
# 回到main继续开发A
git checkout main
git stash pop stash@{1}
# 完成功能A
git add feature-a.js
git commit -m "feat: 功能A完成"
# 切到feature-b继续开发B
git checkout feature-b
git stash pop
(3) 应用暂存到不同分支
▶ 示例:跨分支应用暂存
BASH
# 在feature分支暂存修改
git checkout feature
echo "some changes" > file.js
git stash save "共享修改"
# 切换到main分支应用暂存
git checkout main
git stash apply
# 现在main分支也有这些修改
# 暂存还在,可以应用到其他分支
# 切换到develop分支应用
git checkout develop
git stash apply
# 删除暂存
git stash drop
7. 暂存最佳实践
(1) 暂存使用建议
好的实践:
BASH
# 添加清晰的描述
git stash save "WIP: 用户认证功能,完成登录逻辑"
# 定期清理暂存列表
git stash list
git stash drop stash@{5} # 删除旧的暂存
# 使用apply而不是pop,如果不确定是否还需要
git stash apply
# 暂存前先查看状态
git status
git diff
不好的实践:
BASH
# 不加描述,难以识别
git stash
# 长期保留大量暂存
git stash list # 几十个暂存
# 忘记恢复暂存
git stash
# ... 几天后忘记暂存了什么
(2) 暂存冲突处理
▶ 示例:处理暂存冲突
BASH
# 应用暂存产生冲突
git stash pop
# 输出:
# CONFLICT (content): Merge conflict in src/auth.js
# The stash entry is kept in case you need it again.
# 查看冲突文件
git status
# 解决冲突
# 编辑冲突文件,保留需要的内容
# 标记冲突已解决
git add src/auth.js
# 删除暂存
git stash drop
# 或者放弃暂存
git reset --hard
git stash drop
(3) 暂存管理脚本
▶ 示例:暂存管理工具
BASH
#!/bin/bash
# stash-manager.sh - 暂存管理工具
case "$1" in
list)
echo "=== Stash List ==="
git stash list
;;
show)
if [ -z "$2" ]; then
git stash show -p
else
git stash show -p "stash@{$2}"
fi
;;
apply)
if [ -z "$2" ]; then
git stash apply
else
git stash apply "stash@{$2}"
fi
;;
drop)
if [ -z "$2" ]; then
git stash drop
else
git stash drop "stash@{$2}"
fi
;;
clear)
read -p "Clear all stashes? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
git stash clear
echo "All stashes cleared"
fi
;;
*)
echo "Usage: $0 {list|show [n]|apply [n]|drop [n]|clear}"
;;
esac
❓ 常见问题
Q stash和commit有什么区别?
A stash是临时保存,不在提交历史中,不会推送,不属于任何分支。commit是永久记录,在提交历史中,会推送,属于当前分支。
Q stash pop和stash apply有什么区别?
A pop会应用暂存并删除暂存记录,apply只应用暂存但保留暂存记录。如果需要多次应用同一暂存,使用apply。
Q stash会保存未跟踪的文件吗?
A 默认不会。使用
git stash -u 或 git stash --include-untracked 可以包含未跟踪文件。使用 git stash -a 可以包含所有文件(包括忽略的文件)。Q 如何查看暂存的内容?
A 使用
git stash list 查看暂存列表,使用 git stash show 查看暂存统计,使用 git stash show -p 查看暂存的完整差异。Q 应用暂存时产生冲突怎么办?
A 编辑冲突文件解决冲突,使用
git add 标记冲突已解决,然后 git stash drop 删除暂存。如果想放弃暂存,使用 git reset --hard 和 git stash drop。📖 小节
- 暂存是临时保存工作区修改的功能,用于多任务切换
- 基本操作:
git stash暂存,git stash pop应用并删除 - 带描述:
git stash save "描述"或git stash push -m "描述" - 包含未跟踪:
-u选项包含未跟踪文件 - 查看暂存:
git stash list列表,git stash show详情 - pop vs apply:pop删除暂存,apply保留暂存
- 暂存工作流:暂存当前工作→切换分支→完成任务→恢复暂存
📝 作业
-
基础题:修改文件后使用stash暂存,查看暂存列表和详情,然后应用暂存,体验完整的暂存流程。
-
进阶题:模拟紧急任务切换场景:在开发功能时暂存当前工作,切换分支修复Bug,然后恢复暂存继续开发,体验stash的实际应用。
-
挑战题:体验多任务并行开发:创建多个暂存,在不同分支间切换应用不同的暂存,理解暂存栈的工作原理。