Git 中 stash apply 和 stash pop 的差异

FreeGuideOnline 最新 2026-07-07

bash

假设有一个储藏栈:stash@{0}, stash@{1}

git stash apply stash@{0}

结果:工作区恢复 stash@{0} 的内容,储藏栈不变(仍包含 stash@{0}, stash@{1})

git stash pop stash@{0}

结果:工作区恢复 stash@{0} 的内容,且该记录被移除,栈变为只有原先的 stash@{1}


当你多次使用 `apply` 同一个储藏时,它的修改会反复被应用到当前分支,容易造成冲突或重复修改;而 `pop` 则是一次性的恢复动作,更符合“临时储藏、永久恢复”的直觉。

## 深入理解两个命令的行为细节

了解“是否删除”这一根本差异后,我们还需要关注在复杂场景下它们各自的行为,这直接关系到你能否顺利恢复工作。

### 1. 应用时的状态处理

两个命令在应用状态时遵循相同的逻辑,但 `pop` 在成功应用后会多一步销毁操作。

-   **工作区和暂存区同时恢复**:默认情况下,`stash apply` 和 `stash pop` 都只会将储藏列表中的**工作区修改**应用到当前工作区,而不会恢复你当初执行 `git stash` 时暂存区的内容。也就是说,如果你当时用 `git add` 暂存了某些文件,这些文件的 `staged` 状态会丢失,它们只会以未暂存修改的形式出现。
-   **使用 `--index` 参数恢复暂存状态**:如果你想完整恢复储藏时的现场(包括已暂存的内容),可以为两个命令都加上 `--index` 选项。

    ```bash
    git stash apply --index
    git stash pop --index
    ```

    当你加上 `--index` 后,Git 会尝试将原本在暂存区的文件重新放回暂存区。如果这个过程中发生了冲突(例如暂存区文件与当前分支版本冲突),操作会失败,并且对于 `pop` 来说,储藏记录**不会被删除**,因为应用并未完全成功。这是一个重要的安全保障:`pop` 只有在**干净且完整地应用**之后,才会删除储藏。

### 2. 冲突时的行为差异

当你应用一个储藏,而当前工作分支上的同一部分代码已经发生了改变,就会产生合并冲突。此时两个命令的不同行为非常关键。

-   **`git stash apply` 发生冲突时**:
    -   Git 会像处理合并冲突一样,在有冲突的文件中插入冲突标记(`<<<<<<<`、`=======`、`>>>>>>>`)。
    -   **储藏记录被保留**。你可以在解决冲突后,重新执行 `apply` 或直接丢弃该储藏。
    -   这个行为给了你完全的控制权:你可以随时重新开始,或手动清除冲突后残留的储藏记录。

-   **`git stash pop` 发生冲突时**:
    -   同样会标记冲突文件,需要你手动解决。
    -   **核心区别**:因为应用没有完全成功,所以 Git **不会删除这个储藏记录**。`pop` 命令相当于“安全弹出”:只有成功才真正移走。如果失败,储藏会留在栈中,就像你执行了 `apply` 一样。
    -   你需要先解决冲突(编辑文件并执行 `git add`),然后可以手动删除该储藏记录 `git stash drop`,因为它已经不再需要了。

这个设计保护了你的劳动成果:无论使用 `apply` 还是 `pop`,在冲突解决之前,你储藏的修改都是安全的,绝无丢失风险。冲突场景下两者的临时效果是一样的,区别仅在于解决冲突后:`apply` 需要你记得手动清理,而 `pop` 因为从未删除成功,同样需要你手动 `drop`。

### 3. 对储藏列表管理的影响

日常工作中,使用习惯将直接影响储藏栈的整洁度。

-   **使用 `apply` 的后果**:储藏记录会不断累积。如果你总是用 `apply` 恢复而不手动删除,你的 `stash` 列表会越来越长,里面充斥着大量已经不再需要的“僵尸”储藏。你需要定期执行 `git stash list` 查看,并用 `git stash drop` 或 `git stash clear` 清理。
-   **使用 `pop` 的好处**:它遵循“临时储藏”的天然模型——只需恢复一次即可移除。这让你的储藏列表保持干净,通常只包含那些还未被恢复的、仍然有用的储藏。

因此,在大多数日常的“修复紧急 bug 后回来继续工作”场景中,直接使用 `git stash pop` 是更自然和推荐的做法。

## 实战示例:从储藏到恢复

假设你在 `feature` 分支上修改了 `app.js`,突然需要切换到 `main` 分支。

```bash
# 1. 暂存并储藏所有修改(包含暂存区和工作区)
git stash -u -m "WIP: 优化数据查询"

# 2. 切换到 main 分支处理紧急任务,完成后回到 feature
git checkout main
# ... 修复 bug, 提交 ...
git checkout feature

# 3. 恢复储藏(推荐一次性的 pop)
git stash pop

# 如果发生冲突,Git 会提示并保留储藏记录,解决冲突后手动删除:
# git stash drop stash@{0}

如果你希望保留储藏以备不时之需(例如你想将同一个修改应用到多个分支),可以改用 apply

git stash apply
# 在 feature 分支上测试通过后,切换到另一个分支
git checkout experiment
git stash apply   # 再次应用同一储藏
# 测试完成后,手动清理储藏
git stash drop stash@{0}