Git: Git暂存与多任务切换详解

暂存(Stash)是临时保存工作区修改的功能。当需要切换分支处理紧急任务,但又不想提交未完成的工作时,stash是最佳选择。理解stash的使用对多任务开发非常重要。

1. 暂存基础概念

(1) 什么是暂存

暂存是将工作区和暂存区的修改临时保存起来:

100%
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是一个栈结构:


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

100%
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) 多任务并行开发

100%
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 -ugit 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 --hardgit stash drop

📖 小节


📝 作业

  1. 基础题:修改文件后使用stash暂存,查看暂存列表和详情,然后应用暂存,体验完整的暂存流程。

  2. 进阶题:模拟紧急任务切换场景:在开发功能时暂存当前工作,切换分支修复Bug,然后恢复暂存继续开发,体验stash的实际应用。

  3. 挑战题:体验多任务并行开发:创建多个暂存,在不同分支间切换应用不同的暂存,理解暂存栈的工作原理。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏