Git Stash Pop 深度解析:跨分支工作时如何巧妙地保存进度

本文作为资深开发者的经验分享,旨在深入剖析 Git Stash Pop 命令。文章详细阐述了 Stash 的基本原理、栈结构、与 Apply 的核心区别、冲突处理机制,以及如何利用其进行高级操作和最佳实践。目标是帮助开发者和技术爱好者理解并高效运用 Git Stash,避免在多任务切换和代码管理中出现问题。

阅读时长: 20 分钟
共 9909字
作者: eimoon.com

在日常开发中,我们常常遇到这样的情况:手头的工作还没完成,代码还很零散,但突然来了个急活儿,需要立马切换到另一个分支去处理。这时候,如果直接提交,不仅会留下一个“WIP”(Work In Progress)这种不怎么雅观的提交记录,还会弄脏提交历史。而手动复制粘贴这些改动到文本文件里?那简直是回到了石器时代的操作。

幸好,Git 提供了 git stash 命令,它能优雅地帮我们解决这个难题。git stash 就像是一个临时储物柜,能把我们当前未提交的修改暂时存放起来,让工作区变得干净。等急活儿处理完了,再把之前存起来的改动取回来,一切都和没发生过一样。

这篇文章,我打算深入聊聊 git stash pop 这个命令,它到底是怎么工作的?跟 git stash apply 又有啥不一样?要是遇到冲突了,该怎么化解?这些都是平时我们用 Git 时会碰到的实际问题。

如果你刚接触 Git,觉得 stashing 的概念有点儿绕,那没关系,先熟悉一下 Git 的基本操作再来看,相信你会有更深刻的理解。

初识 Git Stash

简单来说,git stash 就是 Git 为你准备的一个临时存储区,专门用来放那些你还不想提交的修改。

你可以把它想象成代码的“剪贴板”。你有一些修改,还没到提交的时候,但眼下需要切换分支或者拉取远程更新。为了不弄丢你的劳动成果,也不想生成一个临时提交,你就可以把它“藏”起来。这个命令会把你的修改保存好,同时把你的工作目录恢复到最近一次提交的状态。

Git Stashing 的核心思想

Stashing 解决了一个非常实际的问题:Git 不允许你在有未提交修改的情况下切换分支。

比方说,你正在开发一个新功能,写到一半,突然收到一个紧急的 Bug 报告。你得马上切换到 Bug 修复分支去处理,但你本地的代码改了一半,文件也修改了不少。这时候,直接提交肯定不合适,因为工作没完成;强行切换分支又会丢失这些修改。

于是,git stash 就派上用场了。

我个人在以下几种常见场景中,经常会用到 stash:

  1. 紧急任务需要切换分支:你正在开发功能 A,忽然需要去修复一个线上 Bug。
  2. 拉取远程更新:队友推送了新的代码,你需要拉取,但本地有未提交的修改。
  3. 避免脏提交:代码还没完全准备好提交,但你需要一个干净的工作区来进行 rebase 或合并。
  4. 保存实验性代码:想尝试一种新的实现方式,但又不想丢失当前的工作。

Stashing 的好处是它操作起来快,而且可逆,不用你费心去想提交信息或者分支名称。

Stash 栈结构概览

Git 会以“后进先出”(LIFO)的原则来维护你的 stash 列表,这就像一个栈。

每次你创建一个 stash,Git 都会在栈顶添加一个新的条目。最新的 stash 会被标记为 stash@{0},而更早的 stash 则会依次向下移动,变成 stash@{1}stash@{2},以此类推。

从底层来看,每个 stash 实际上都是一次提交(或者说,是一组提交)。Git 会创建一个多父提交结构,用于存储:

  1. 你的工作目录修改。
  2. 你暂存区(index)的修改。
  3. 未跟踪文件(如果你使用了 -u 选项)。

这种结构意味着 stash 不仅仅是快照,它们是完整的提交,Git 之后可以把它们重新合并回你的工作目录。

你可以通过两种方式引用 stash:一种是使用 stash@{0} 这样的索引语法,另一种是使用 git stash list 命令找到你当时给 stash 的消息,然后引用那个特定的 stash。对于最近的 stash,索引方式更快,但当你保存了多个 stash 时,自定义消息无疑能让你更好地分辨。

通常,一个 stash 栈看起来大概是这样:

Image 1 - Example git stash list output

那个 WIP on 前缀表明你直接用了 git stash 命令,没有附带任何消息。而 On 前缀则说明你通过 git stash push -m 提供了自定义消息。

Stash 只存在于你的本地仓库,它们不会被推送到远程。这意味着你的临时工作只在你自己的机器上,随时准备在你需要时恢复。

创建、管理与检查 Stash

git stash 这个基础命令虽然能用,但用起来有点儿“懒”。你最终会得到一堆“WIP on branch-name”这样的条目,完全看不出里面到底藏了什么东西。要是六周后再来看,你肯定不记得哪个 stash 包含了 Bug 修复,哪个又是你改到一半的重构代码。

现在,我来教你如何从一开始就把它做好。

使用恰当的元数据创建 Stash

最简单的 stash 命令就是直接敲 git stash

git stash

这个命令会保存你的改动,但像“WIP on main”这样的信息,说实话,没啥实际意义。你不查看内容就根本不知道里面是啥。

我的建议是,用 git stash push -m 命令,加上一段描述性的消息:

git stash push -m "更新了文件 file.txt"

这样一来,你的 stash 列表就变得一目了然了:

Image 2 - Git stash with and without a message

两者的区别非常明显。一个明确告诉你做了什么,另一个则让你费尽心思去猜。

写 stash 消息时,就像是给未来的自己留便签。比如“添加认证逻辑”、“修复导航栏 Bug”或者“尝试新的 API 设计”,这些都比那些通用的 WIP 消息强上百倍。

包含未跟踪或已忽略的文件

默认情况下,git stash 只会保存 Git 已经跟踪的文件。

未跟踪的文件会保留在你的工作目录中。这常常让人感到意外:当他们 stash 后切换分支,发现新文件竟然还在那里。

下面是一个会让你感到意外的例子:

echo "new_feature.py" > new_feature.py
git stash

这个文件没有被 stash 起来,因为 Git 还没跟踪它:

Image 3 - Trying to stash an untracked file

要解决这个问题,你需要使用 -u 选项来 stash 未跟踪文件:

git stash push -u -m "为很酷的新功能添加了一个 Python 文件"

这样,Git 就会把已跟踪文件和未跟踪文件都 stash 起来。你的工作目录会变得完全干净。

Image 4 - Stashing an untracked file

小贴士:你也可以加上 -a 选项来包含被忽略的文件。它会 stash 掉所有已跟踪文件、未跟踪文件以及 .gitignore 中指定的文件。不过,你很少会需要这个选项,除非你正在调试构建问题或者需要一个完全干净的、包含所有内容的快照。

我给大家准备了一个速查表,让你知道什么时候用哪个选项:

  1. 不带任何选项:你只修改了现有文件。
  2. -u 选项:你添加了尚未被跟踪的新文件。
  3. -a 选项:你需要保存所有东西,包括构建输出或临时文件。

绝大多数情况下,你都会用到 -u。当你在开发过程中创建了新文件时,它通常是最安全的选择。

部分或选择性 Stashing

你并不需要把工作目录里的所有东西都 stash 起来。

比方说,你修复了两个 Bug,但只想 stash 其中一个。或者你修改了五个文件,但只有三个文件与你打算保存的功能相关。

你可以通过路径规范(pathspec)来 stash 指定的文件:

git stash push -m "新文件" new_file.txt new_file_2.txt

这个命令只会 stash new_file.txtnew_file_2.txt。其他文件都会保留在你的工作目录中。

Image 5 - Stashing only selected files

你也可以使用通配符:

git stash push -m "Stash 所有 txt 文件" *.txt

或者,你还可以使用 --patch 进行交互式 stashing:

git stash push --patch

Git 会逐个提示你的每一次改动,询问你是否要 stash 它。你可以回答 y(是)、n(否),或者 s(将这个 hunk 拆分成更小的块)。

当你在同一个文件里混杂了相关和不相关的改动时,这个功能非常方便。你可以把功能性的工作 stash 起来,而把调试代码留在工作区。

下面是交互式提示的模样:

Image 6 - Patch example

列出与检查 Stash

正如你已经看到的那样,运行 git stash list 可以查看你所有的 stash:

Image 7 - Listing stashes

最新的 stash 永远是 stash@{0}。旧的 stash 会依次向下移动索引。

如果你给 stash 写了好的描述信息,你就能一眼扫过这个列表,清楚每个 stash 里都有什么。如果没写,那你就得一个接一个地去检查了。

使用 git stash show 命令可以查看摘要:

Image 8 - Stash summary

这会显示哪些文件发生了变化,以及增加了或删除了多少行。

另外,你也可以使用 git stash show -p 来查看完整的差异:

Image 9 - The entire stash difference

-p 选项会显示实际的代码改动,就像 git diff 一样。在你运行 git stash pop 之前,用这个命令确认你正在弹出正确的 stash 是个很好的习惯。

Git Stash Pop 到底做了什么?

现在,我们来聊聊 git stash pop 到底做了什么,以及你为什么会用它。

基本行为与语法

git stash pop 命令主要做了两件事:它应用了你藏起来的改动,然后从栈中删除了那个 stash。

这是基本的语法:

git stash pop

这个命令会弹出最新的 stash (stash@{0}),并将其从你的 stash 列表中移除。

你也可以通过引用其索引来弹出特定的 stash:

git stash pop stash@{1}

这会应用 stash@{1} 并将其移除。但这里有个小细节:剩下的 stash 会向上移动。原先的 stash@{2} 会变成 stash@{1},以此类推。

Image 10 - Popping a specific stash

Git 如何处理 Stash Pop

当你运行 git stash pop 时,Git 在后台会执行一个三方合并(three-way merge)。

它会比较三种状态:

  1. 基础提交(你创建 stash 时的那个提交)。
  2. 你当前 HEAD 的提交。
  3. 你 stash 起来的修改。

如果合并成功,没有冲突,Git 就会应用你的改动并移除 stash。你的工作目录会恢复到你 stash 之前的状态。

这是一个简单的操作流程示例:

# 你正在开发一个功能
echo "新的功能代码" >> feature.py
git stash push -m "功能开发中,未完成"

# 切换到新分支并进行无关的修改
git checkout bugfix
echo "修复 Bug" >> bugfix.py
git add bugfix.py
git commit -m "修复关键 Bug"

# 回到原来的分支
git checkout main

# 弹出你 stash 的工作
git stash pop

如果 Git 无法干净地合并,你就会遇到冲突。由于 pop 操作未能成功完成,stash 会继续留在你的栈中。你会在文件中看到冲突标记,就像平时处理常规合并冲突一样。

当出现冲突时,你需要手动解决它们,暂存已修复的文件,然后提交。因为 stash 不会自动消失,所以在解决完所有问题之后,你还需要手动运行 git stash drop

跨分支应用 Stash

Stash 本身并不与任何特定分支绑定。

你可以在 bugfix 分支上创建一个 stash,然后切换到 main 分支,再在那里弹出这个 stash。Git 不在意这个 stash 是从哪里来的,它只会尝试将这些改动应用到你当前所在的分支上。

这会带来一些非常实用的工作流:

  1. 在分支间移动工作:假设你在错误的分支上开始写代码了。与其重置并丢失工作,不如 stash 起来,切换到正确的这分支,然后在那儿弹出。
  2. 重组任务:你可能在一周前 stash 了一些实验性代码。现在你想在另一个分支上试试看,看它在那里是否表现更好。
  3. 在不同上下文中测试改动:你有一个 Bug 修复的 stash,希望在提交之前在多个分支上测试它。这时候,你可以使用 git stash apply 而不是 pop,这样 stash 会保留下来,你可以在不同分支上测试,最后在你打算提交的那个分支上 pop 掉。关于 apply,我们稍后会详细讲讲。

只要这些改动不与你当前分支发生冲突,stash 就能干净地应用——这是你需要记住的核心思想。

Git Stash PopGit Stash Apply 之辨

开发者们经常会纠结,到底是用 git stash pop 还是 git stash apply 呢?

关键差异:Stash 的持久性

它们之间的主要区别其实很简单:pop 在应用 stash 后会将其从栈中移除,而 apply 则会保留它。

pop 的时候,你其实是在说:“我搞定这个 stash 了,用完就扔掉。”而用 apply 的时候,你是在说:“我可能以后还会再用到这个。”

这会以两种方式影响你的工作流程:

  1. 安全性:如果 pop 过程中出了问题,你需要恢复,那么 stash 就不见了。而 apply 会让 stash 保留在你的栈中,作为一份备用。
  2. 清理:使用 pop 可以自动保持你的 stash 列表整洁。而使用 apply 则意味着你需要在完成后手动运行 git stash drop 来移除那些你已经用完的 stash。

大多数开发者默认使用 pop,因为它更干净。但如果你不确定 stash 能否在当前分支上顺利应用,或者想在多个分支上测试相同的改动,apply 会是更好的选择。

可预测性、风险与索引的变动

pop 有个比较棘手的地方——当你从栈的中间弹出 stash 时,stash 的索引会发生变化。

比方说,你现在有三个 stash:

stash@{0}: 功能开发
stash@{1}: Bug 修复
stash@{2}: 实验性代码

如果你弹出 stash@{1},剩下的 stash 就会向上移动:

stash@{0}: 功能开发
stash@{1}: 实验性代码

这可能在你编写 Git 脚本或连续运行多个 pop 命令时引发问题。你弹出了 stash@{1},然后试图弹出 stash@{2},但那个索引已经不存在了。

apply 命令就没有这个问题。索引只在你明确运行 git stash drop 时才会改变。你可以以任何顺序应用 stash,而不用担心栈在不知不觉中发生变化。

冲突处理方式相同

这两个命令在处理冲突时的行为是完全一样的。

如果 Git 无法干净地合并你 stash 的改动,你的文件里就会出现冲突标记。区别在于 stash 之后的命运:

  1. 使用 pop:stash 会留在你的栈中,因为 pop 操作没有成功完成。
  2. 使用 apply:stash 会留在你的栈中(反正它本来就会留在那里)。

无论哪种情况,你都需要手动解决冲突,用 git add 暂存已解决的文件,然后(如果你使用的是 apply)用 git stash drop 移除 stash。

冲突解决的工作流是完全相同的——唯一的区别是 stash 是否会在成功时自动被移除。

何时选择哪个?

我个人建议你在以下情况使用 git stash pop

  1. 你只需要应用 stash 一次,并且用完就没用了。
  2. 你想让你的 stash 列表自动保持整洁。
  3. 你正在单个分支上工作,不需要将 stash 应用到其他地方。

git stash apply 则适用于以下场景:

  1. 你需要在多个分支上测试相同的改动。
  2. 你不确定 stash 能否干净地应用。
  3. 你正在编写 Git 操作脚本,需要可预测的索引。
  4. 你需要一个安全网,以防出现问题。

处理 Stash Pop 中的冲突

当 Git 无法干净地合并你 stash 的改动时,冲突就会发生。本节我来给大家讲讲如何处理它们。

冲突何时发生以及为何发生

当你弹出 stash 时,Git 会执行三方合并。这个过程中就可能发生冲突。

Git 会比较你代码的三个版本:

  1. 你创建 stash 时的那个基础提交。
  2. 你当前 HEAD 的提交。
  3. 你 stash 起来的修改。

如果同一个地方在当前分支和 stash 中都被修改了,Git 就会不知道该保留哪个版本。这时候,冲突就产生了。

Stash pop 冲突的常见原因包括:

  1. 你在两个分支上修改了同一个文件:你 stash 了一个文件的改动,切换了分支,然后在弹出 stash 之前又编辑了同一个文件。
  2. 别人推送了改动:你 stash 了工作,然后从远程拉取了更新,而这些更新也修改了相同的代码。
  3. 你执行了 rebase 或 cherry-pick:Git 预期的基础提交不存在了,导致合并不匹配。

总而言之,一个 stash 存放的时间越久,当你最终弹出它时,遇到冲突的可能性就越大。

冲突解决工作流

当冲突发生时,Git 会暂停 pop 操作,并在你的文件中标记冲突:

你的文件现在会包含冲突标记:

所有 <<<<<<< Updated upstream======= 之间的内容是你的当前代码。而 =======>>>>>>> Stashed changes 之间的内容则来自 stash。

下面是你如何修复它的方法:

  1. 打开冲突文件,决定保留哪个版本(或者将两者结合起来)。
  2. 移除冲突标记(<<<<<<<=======>>>>>>>)。
  3. 使用 git add 暂存已解决的文件。
  4. 使用 git stash drop 手动删除 stash——记住,它不会自动消失。

替代策略

如果你不想在当前分支处理冲突,可以使用 git stash branch 命令。

这个命令会从你创建 stash 的那个提交创建一个新分支,然后将 stash 应用到这个新分支上:

git stash branch temp-feature

Git 会创建一个名为 temp-feature 的分支,切换到这个新分支,然后将你的 stash 弹出到上面。由于这个分支从你 stash 时的那个精确提交开始,所以不会有任何冲突。我会在后面的章节中更深入地讨论这一点。

你也可以使用 git reset --merge 来中止一个失败的 pop 操作:

git stash pop

# 出现冲突

git reset --merge

这会撤销 pop 尝试,并将你的工作目录恢复到 pop 之前的状态。stash 依然会留在你的栈中。

中止进行中的 Stash 操作

如果你开始了一个 pop 或 apply 操作,但又想完全撤销,你还是有办法的。

对于你还没开始解决的冲突,运行以下命令:

git reset --merge

这会取消合并并清理你的工作目录。所有内容都会回到你运行 stash 命令之前的样子。

对于你已经部分解决的冲突,运行这个命令:

git reset --hard HEAD

这会丢弃你工作目录中的所有改动,包括你已经做出的任何修复。当你一团糟,只想重新开始时,就可以使用这个命令。

请记住,这个命令会销毁未提交的工作。在运行之前,请确保你真的想把所有东西都丢弃。

中止操作后,你的 stash 仍然在栈中(除非你使用了 pop 并且它在遇到冲突之前成功了)。你可以稍后再次尝试,或者使用 git stash branch 采取更干净的方法。

清理 Stash

在长期项目中,stash 会迅速堆积起来。你 stash 了一些工作去修复 Bug,然后就忘了它。你创建了一些实验性的 stash,却再也没弹出过。六个月后,你可能就有 20 个 stash,却不知道其中一半是干嘛用的。

这会让你更难找到你真正需要的 stash。你最终会一个接一个地运行 git stash show -p,只是为了弄清楚里面有什么。

这简直是浪费时间,所以请记住要定期清理你的 stash。

删除单个 Stash

使用 git stash drop 来删除特定的 stash:

git stash drop stash@{2}

这会从你的栈中删除 stash@{2}。剩下的 stash 会向上移动——原来的 stash@{3} 会变成 stash@{2}

你也可以不指定索引来删除最新的 stash:

git stash drop

这会默认删除 stash@{0}

通过我多年的 Git 工作经验,我删除 stash 的主要原因通常是:

  1. 你意识到你不再需要这些改动了。
  2. 你创建了重复的 stash(这种情况比你想象的要多)。
  3. 一个 stash 太旧了,你已经不记得它是干什么用的了。

如果你不确定是否要删除一个 stash,最好先用 git stash show -p 检查一下。小心驶得万年船。

清空整个 Stash 栈

你可以使用 git stash clear 命令一次性删除所有 stash:

git stash clear

但请记住,这是永久性的操作。你无法撤销它,也无法在之后恢复这些 stash。

只在你百分之百确定你不再需要任何已 stash 的工作时,才使用 git stash clear

大多数情况下,最好是单独删除 stash,以免不小心删除了你需要的东西。

从 Stash 创建分支

有时候,最干净的解决方案是把 stash 变成它自己的分支。这样可以避免合并冲突,并给你的 stash 工作一个合适的归宿。

使用 git stash branch

git stash branch 命令会创建一个新分支,并将你的 stash 应用到上面:

下面是具体发生的过程:

  1. Git 会从你创建 stash 的那个提交,创建一个名为 feature-experiment 的新分支。
  2. Git 会切换到那个新分支。
  3. Git 会将 stash 弹出到这个分支上。
  4. Git 会从你的栈中移除这个 stash(就像 pop 一样)。

由于新分支从与 stash 相同的提交开始,所以不会有任何冲突。改动每次都能干净地应用。

你也可以使用特定的 stash,而不是最新的那个:

git stash branch new-feature stash@{2}

常见使用场景

从 stash 创建分支对于实验性功能来说再合适不过了。比如说,你在一周前 stash 了一些实验性代码,现在你想好好地开发它,又不想把当前的工作搞乱。

你就能得到一个干净的分支来开发这个实验性功能。如果成功了,就合并回去;如果不行,就删除分支,继续前进。

它对于长时间的 rebase 也很有用。与其在 rebase 过程中处理潜在的冲突,不如把所有东西都 stash 起来,然后从它创建一个分支。

git stash push -m "WIP, rebase 之前"
git rebase main
git stash branch temp-work

现在你就可以单独审查 stash 的改动,并在 rebase 之后决定如何整合它们。

创建分支对于隔离大型或混乱的改动也很有帮助。想象一下,你有一个巨大的 stash,包含了 15 个文件的改动,其中一半与你当前的分支冲突。这时候,直接创建一个新分支就省心多了,不用再费劲去解决冲突。

你就能得到一个专门的分支,可以在上面从容地审查所有改动,正确提交,然后再以清晰的历史记录合并回去。

高级技巧与恢复选项

现在你已经熟悉了 git stash 的基本操作,接下来我将带你看看一些更高级的概念。

处理特定的 Stash 条目

你可以弹出或应用栈中的任何 stash,而不仅仅是最新那个。

只需通过它们的索引来引用 stash,就能精确地获取你想要的东西:

git stash pop stash@{3}
git stash apply stash@{1}

这让你可以在不触及更新 stash 的情况下,获取较旧的 stash。当你弹出一个中间的 stash 时,它下面的所有索引都会向上移动。原先的 stash@{4} 会变成 stash@{3},以此类推。如果你正在编写 Git 操作脚本或连续弹出多个 stash,这种索引变动会打乱你的命令。

解决方案是,当你需要可预测的索引时,使用 apply 而不是 pop

git stash apply stash@{2}

# 完成后手动删除它
git stash drop stash@{2}

这让你对栈何时改变有了完全的控制。你应用 stash,验证它是否正常工作,然后自己删除它。索引只在你明确告诉它们改变时才会移动。

恢复暂存状态

默认情况下,git stash popgit stash apply 会恢复你的改动,但不会保留暂存状态。

假设你修改了三个文件,并在 stash 之前将其中两个文件暂存准备提交。当你弹出那个 stash 时,所有三个文件都会作为未暂存的改动回来。Git 会丢失你想要提交哪些文件的记录。

解决方案是使用 --index 标志,以精确地恢复暂存状态:

git stash pop --index

这会精确地重建你 stash 之前的完整工作树和索引。已暂存的文件保持暂存状态,未暂存的文件保持未暂存状态。一切都恢复到它原来的样子。

你会在以下情况下需要这个标志:

  1. 你正在准备一个精心暂存的提交,并且不能丢失暂存信息。
  2. 你正在处理复杂的暂存场景,比如你使用了 git add -p 来暂存文件中特定的 hunk。
  3. 你希望你的工作目录看起来和 stash 之前一模一样,没有任何例外。

大多数情况下,你不会需要这个标志。你只会弹出 stash,看看回来了什么,然后暂存你需要的东西。

从 Stash 错误中恢复

你最终还是会不小心删除你需要的 stash,或者意外清空整个栈。

如果发生这种情况,Git 的 reflog 可以挽救你的局面。

reflog 跟踪你仓库中所有的引用变更,包括 stash。当你创建或删除 stash 时,Git 会将其记录在 reflog 中。即使你删除了一个 stash,这些提交仍然会作为无法访问的对象存在一段时间。

你可以通过搜索无法访问的提交来找回你丢失的 stash:

git fsck --unreachable | grep commit

Searching for unreachable commits

现在你可以检查每个提交来找到你的 stash:

git show 066ded985342e585bc8e75c074add99ec1950f50

查看差异和提交消息,以识别哪个是你丢失的 stash。一旦你找到正确的提交,就可以恢复它:

git stash apply 066ded985342e585bc8e75c074add99ec1950f50

这会将丢失的 stash 应用到你的工作目录,就像它从未被删除过一样。

更好的是,从一开始就遵循这些最佳实践来防止错误发生:

  1. 编写描述性的 stash 消息,这样你就确切地知道你在删除什么。“修复登录 Bug”比“WIP”更能说明问题,特别是当你在看一堆五个 stash 的列表时。
  2. 在删除之前使用 git stash show -p 来确认它是正确的 stash。花五秒钟验证,总比花五分钟恢复要好。
  3. 定期清理你的 stash 栈,以防它变得混乱。一个包含三个相关 stash 的栈比一个包含 15 个旧条目的栈更容易管理。
  4. 当你不是百分百确定要删除 stash 时,使用 apply 而不是 pop。你总可以在验证一切正常后,再手动删除它。

Reflog 恢复是可行的,但它很繁琐。看看上面的图片,你就会发现我得手动检查多少提交——而这只是一个为了本文目的而创建的小项目。

最佳实践与工作流指南

有效地使用 stash 意味着遵循那些能防止错误并保持工作流程清晰的模式。我来给大家分享一些我常用的方法。

推荐的工作流模式

最安全的 stash 工作流遵循一个简单的模式:带消息地 stash,切换上下文,然后在准备好继续时弹出。

# 在 feature-branch 上工作
git stash push -m "功能开发进行中"

git checkout main
# 在 main 上进行紧急工作

# 返回
git checkout feature-branch
git stash pop

这适用于大多数情况。你保存了你的工作,处理了中断,然后干净地恢复了一切。

另一种常见的模式是“检查后再弹出”工作流。你列出你的 stash,检查里面是什么,然后弹出正确的那个。

git stash list
git stash show -p stash@{1}
git stash pop stash@{1}

这个额外的验证步骤可以防止你弹出错误的 stash 或应用过时的工做。

有些开发者更喜欢“不情愿的提交”作为 stashing 的替代方案。他们不是 stash,而是创建一个带有“WIP - 尚未准备好”等消息的临时提交,然后稍后修改或重置它。如果你对 stash 不习惯,或者希望工作在分支历史中被跟踪,这种方式是可行的。缺点是,如果你忘记清理,最终会得到混乱的提交日志。

与开发工具集成

大多数 Git GUI 都会可视化地显示你的 stash,并允许你通过点击来弹出或应用它们。

像 GitKraken、Sourcetree 和 GitHub Desktop 这样的工具会在侧边栏列出你的 stash。你可以看到消息,检查差异,以及在不输入命令的情况下应用或删除 stash。当你有很多 stash 并且想并排比较它们时,这非常方便。

VS Code 的 Git 扩展会在源代码控制面板中显示 stash。你可以右键点击来弹出、应用或删除,而无需离开你的编辑器。

如果你更喜欢命令行,Shell 别名(alias)可以加快 stashing 的速度。把这些添加到你的 .bashrc.zshrc 文件中:

alias gst='git stash'
alias gstp='git stash pop'
alias gstl='git stash list'
alias gsts='git stash show -p'

现在,gst 会 stash 你的工作,gstp 会弹出它,gstl 会列出你的 stash,而 gsts 会显示差异。每次你操作时都能省下一些按键。

Stash 卫生与团队考量

Stash 仅限于你的本地机器——它们永远不会被推送到远程仓库。

这意味着你的队友看不到你的 stash,你也不能从另一台电脑访问它们。如果你需要共享进行中的工作,不要 stash 它。而是使用补丁或临时分支。

要通过补丁共享工作,运行以下命令:

git stash show -p > my-work.patch
# 将补丁文件发送给你的队友
# 他们用以下命令应用:git apply my-work.patch

现在把补丁文件发送给你的队友,他们会通过运行 git apply my-work.patch 来应用它。

要通过临时分支共享工作,运行此命令:


git checkout -b temp/work-in-progress
git add .
git commit -m "WIP - 认证功能"
git push origin temp/work-in-progress

你的队友可以拉取分支,审查工作,然后从那里继续。

无论你选择哪种方法,都要始终保持良好的 stash 卫生。这将避免任何混乱,并让你的本地栈保持易于管理。以下是一些值得遵循的好习惯:

  1. 为每个 stash 使用描述性的名称。“添加用户认证逻辑”能告诉你里面是什么。“WIP on feature-branch”则什么也说不清楚。
  2. 每周清理你的 stash。运行 git stash list 并删除任何超过一周的 stash。如果你七天内没用到它,你可能根本就不需要它了。
  3. 不要让你的 stash 数量超过五个。超过五个 stash 意味着你正在把它们当作长期存储,而不是临时存放。要么弹出它们,要么提交工作,要么删除它们。
  4. 如果你在一个团队中工作,当有人问你“你正在做的代码在哪里?”时,绝不能回答“它在我的 stash 里。”Stash 是私有的、临时的——它们不用于共享或存档重要的工作。

结语

现在,你已经彻底了解了 git stash pop 是如何工作的以及何时使用它。

从根本上说,stashing 很简单:保存你未提交的工作,切换上下文,然后稍后再恢复。但细节很重要。了解何时使用 pop 对比 apply,如何处理冲突,以及何时从 stash 创建分支,将为你节省无数的挫折时间。

最重要的收获是,stash 只是临时存储,不是永久备份。使用描述性的消息,定期清理,并且不要让你的 stash 栈超过五个条目。如果你需要共享工作或长期保存,请将其提交到分支中。

关于

关注我获取更多资讯

公众号
📢 公众号
个人号
💬 个人号
使用 Hugo 构建
主题 StackJimmy 设计