第八章. Git Stash 教程:如何临时保存工作现场与恢复进度

第八章. Git Stash 教程:如何临时保存工作现场与恢复进度

摘要:在实际开发中,“被中断” 是常态。无论是紧急的线上 Bug 修复,还是同事的代码审查请求,我们经常需要在当前代码尚未完成时切换上下文。本章将深入 Git 的 “现场保护” 机制。我们将摒弃将 Stash 视为简单 “剪贴板” 的浅层认知,从对象存储的视角解构它作为一个 特殊 Commit 栈 的物理特性;同时,我们将引入企业级并行的杀手锏 —— git worktree,学习如何在不切换分支的情况下,通过物理隔离的工作区同时处理多个任务,彻底解决频繁切换环境带来的编译缓存失效与依赖冲突问题。

本章学习路径

  1. Stash 解剖:通过底层命令剖析 Stash 生成的 Commit 对象结构,理解其 “双父节点” 的存储原理。
  2. 精准现场:掌握处理 “未追踪文件” 与 “部分暂存” 的高级现场保护技巧,避免新文件丢失。
  3. 冲突突围:学习当 stash pop 引发冲突时,如何利用 stash branch 开辟独立战场解决问题。
  4. 物理并行:从单工作区进化为多工作区(Worktree),实现文件系统级别的并行开发。
  5. 场景选型:建立 Stash(轻量级切换)与 Worktree(重型并行)的最佳实践决策树。

8.1. 储藏(Stash)的底层对象结构与栈管理

在之前的章节中,我们一直遵循“修改 -> 暂存 -> 提交”的线性工作流。但在真实的职场中,这种理想状态经常被打破。我们需要一种机制,能够让我们在“代码写了一半、甚至无法编译”的状态下,安全地暂停当前工作,去处理更紧急的任务。本节我们将学习 git stash,并深入探索它在 .git 目录下的真实面目。

8.1.1. 为什么需要 Stash?

让我们先还原一个最真实的开发场景,来理解为什么简单的 Commit 无法满足所有需求。

场景模拟:进退两难的 10 分钟

假设你正在 feature-user 分支上开发用户模块,工作进行了 50%:

  • 修改了 User.java(逻辑只写了一半,甚至花括号都没闭合)。
  • 新建了 UserTest.java(还没来得及 git add)。

突然,项目经理冲过来说:“线上支付接口崩了,必须在 10 分钟内修复!请立刻切到 hotfix 分支!”

此时你面临一个两难的选择:

  1. 直接提交(Commit)

    • 后果:你会把一份无法编译的垃圾代码提交到版本库。这不仅污染了提交历史,如果配置了 CI(持续集成),还会直接导致构建失败,引发团队报警。
  2. 放弃修改直接切换(Checkout)

    • 后果:Git 会报错 error: Your local changes to the following files would be overwritten by checkout,阻止你切换。如果你强行使用 -f 强制切换,你的一下午心血就全部丢失了。

Stash 的定义

git stash 就是为了解决这个问题而生的。它就像一个“时间冻结器”:

  1. 它会将你当前 工作区(Worktree)和 暂存区(Index)的所有修改打包。
  2. 它将这些修改保存到一个独立的 (Stack)中。
  3. 它会将你的工作目录瞬间 重置(Reset)到当前分支最后一次提交(HEAD)的干净状态。

这样,你就可以毫无负担地切换分支去修 Bug,修完回来后,再从栈中把之前的进度“取”出来。

8.1.2. Stash 的物理本质:悬空的 Commit 对象

新手常会以为 Stash 是 Git 开辟的一块“临时内存”或者“剪贴板”。但作为进阶开发者,我们需要看透它的本质。

Stash 的本质是一个特殊的 Commit 对象。

没错,当你执行储藏操作时,Git 实际上是在后台悄悄帮你生成了 2 个(有时是 3 个)提交对象。这些提交对象并不在任何分支的历史线上(没有分支指向它们),而是由 .git/refs/stash 这个特殊的引用直接管理。

【实战演练】透视 Stash 内部构造

为了验证这一点,我们先在一个干净的仓库中制造一些修改,然后执行储藏。

步骤 1:制造现场

1
2
3
4
5
6
7
8
9
# 1. 确保在 master 分支且干净
git switch master

# 2. 修改一个已追踪的文件(模拟工作区变更)
echo "WIP content" >> README.md

# 3. 将另一个文件加入暂存区(模拟暂存区变更)
echo "Staged content" > staged.txt
git add staged.txt

此时,我们的 工作区暂存区 都有了未提交的内容。

步骤 2:执行储藏

这里我们使用标准的储藏命令(稍后会详细讲解):

1
git stash push -m "Debug: 分析 Stash 结构"

步骤 3:获取 Stash 的哈希值

我们来看看 refs/stash 到底指向了什么。

1
git rev-parse refs/stash

假设输出的哈希值是 5dc6866ef3daeed7691c9e5c5bec1bb55bc867e0

步骤 4:解剖 Commit 对象

现在,我们用底层命令 git cat-file 来查看这个对象的详细信息。

1
2
# 请将 <hash> 替换为你实际看到的哈希
git cat-file -p <hash>

输出解析(重点!!!):

1
2
3
4
5
6
7
tree 4d5e6f...
parent 7h8i9j... <-- 父节点 1:HEAD(你储藏前所在的那个提交)
parent 1k2l3m... <-- 父节点 2:Index(储藏时暂存区的状态)
author ...
committer ...

On master: Debug: 分析 Stash 结构

原理图解

这里揭示了 Stash 的核心秘密。一个标准的 Stash 对象通常是一个 Merge Commit(合并提交),它有两个父节点:

  1. HEAD Parent:记录了你执行 stash 命令时,当前分支指向的那个提交。这保证了恢复时能找到“根”。
  2. Index Parent:Git 会先将你 暂存区 的内容生成一个独立的 Commit 对象,作为 Stash 的第二个父节点。

而 Stash Commit 本身(即 tree 指向的内容),则记录了你 工作区 的修改。

这种“WIP Commit(工作区) + Index Commit(暂存区)”的双重结构,使得 Git 能够完美地还原你当时“哪些文件 add 了,哪些没 add”的精确状态。

8.1.3. 标准化操作:压栈、查看与出栈

理解了 Stash 是一个 Commit 对象后,我们再来学习如何优雅地管理这个“堆栈”。

1. 压栈(Push):拒绝无名储藏

在旧版本的教程中,你可能会看到 git stash save。但在 2025 年的今天,请全面拥抱 git stash push

为什么要废弃 save
save 命令对路径规格(Pathspec)的支持非常糟糕,无法精确指定“只储藏某个文件”。而 push 不仅语义清晰,还完全支持像 git add 一样的路径过滤器。

错误示范

1
git stash

列表里会显示:stash@{0}: WIP on master: ...。一周后你根本不知道这是啥,不敢删也不敢用,最后变成垃圾数据。

正确示范

1
git stash push -m "feat: 暂停用户页开发,去修支付Bug"

养成必加 -m 注释的习惯,是对自己记忆力的尊重。

2. 查看栈(List):Reflog 的应用

执行多次储藏后,我们可以查看栈列表:

1
git stash list

输出示例

1
2
3
stash@{0}: On master: feat: 暂停用户页开发,去修支付Bug
stash@{1}: On dev: fix: 尝试修复登录问题,但失败了
stash@{2}: On master: docs: 临时保存文档草稿
  • 栈顶(Top)stash@{0} 是最新存入的。
  • 栈底(Bottom):数字越大,存入时间越早。
  • 原则:这是一个 LIFO(后进先出) 的栈结构。

3. 出栈:Pop、Apply 与 Drop 的抉择

当我们需要恢复现场时,有三个命令可选,它们的区别关乎数据的安全性。

选项 A:git stash pop(最常用,但有风险)

  • 行为:尝试应用栈顶(stash@{0})的变更。如果应用 成功(无冲突),则从栈中 删除 该储藏。
  • 风险:如果发生冲突,Git 会保留栈中的储藏,让你解决冲突。但很多新手误以为一旦报错就都没了,导致惊慌失措。

选项 B:git stash apply(最安全)

  • 行为:尝试应用栈顶变更,但 绝不删除 栈中的记录。
  • 场景:当你需要在多个分支上应用同一个补丁(比如把一份调试配置应用到 master 和 dev)时使用。或者你对这次合并没底,想给自己留条后路。

选项 C:git stash drop(清理)

  • 行为:直接删除栈顶(或指定位置)的储藏。
  • 场景:当你确定某个储藏已经没用了,手动清理垃圾。

代码实战:指定位置恢复

如果你想恢复列表中的第二个储藏(stash@{1}),而不是最新的:

1
2
3
4
5
# 应用 stash@{1},但不删除它
git stash apply stash@{1}

# 验证无误后,手动丢弃它
git stash drop stash@{1}

8.1.4 本节小结

核心要点

  • Stash 是为了解决“临时中断”而生的,它避免了提交未完成的代码。

  • Stash 的物理本质是一个多父节点的 Merge Commit,分别记录工作区和暂存区。

  • 必须使用 git stash push -m 添加注释,拒绝匿名储藏。

  • 区分 pop(应用并删除)与 apply(应用并保留)的使用场景。

速查代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 带注释压栈
git stash push -m "message"

# 2. 查看列表
git stash list

# 3. 恢复栈顶并删除
git stash pop

# 4. 恢复特定记录不删除
git stash apply stash@{1}

# 5.手动删除栈
git stash drop stash@{1}

8.2. 进阶 Stash:精细化控制与冲突逃生

在上一节中,我们掌握了 Stash 的基础用法,能够处理常规的“暂停与恢复”。但在复杂的工程实践中,粗暴的 stash push 往往会带来新的灾难:新创建的文件莫名丢失?只想储藏代码却把调试日志也带走了?或者在恢复现场时遭遇了满屏的冲突红字?

本节我们将深入 Stash 的高级参数,学习如何精准控制储藏范围,并掌握一种能够完美避开冲突的“逃生通道”。

8.2.1. 覆盖盲区:找回“消失”的新文件

这是新手使用 Stash 时最容易遇到的“灵异事件”。

场景模拟:新文件的离奇失踪

假设你正在开发一个新功能:

  1. 修改了已有的 Controller.java
  2. 创建了一个全新的 Service.java(但在 IDE 中还没来得及执行 Add)。

此时你执行了标准的 git stash push 并切换了分支。等你切回来执行 git stash pop 时,你会惊恐地发现:Controller.java 的修改回来了,但 Service.java 不见了!

核心原因:默认情况下,git stash 仅储藏被 Git 追踪(Tracked)的文件。对于未被追踪(Untracked)的新文件,Git 认为它们不属于版本控制的管辖范围,因此在打包现场时直接忽略了它们。当你执行 reset 动作(Stash 的最后一步)或者切换分支时,这些无主的文件很容易被清理或遗忘。

解决方案:扩大储藏范围

为了解决这个问题,Git 提供了专门的参数来扩展储藏的“拾取范围”。

1. 包含未追踪文件 (-u)

这是最推荐的日常用法。

1
2
# -u 是 --include-untracked 的缩写
git stash push -u -m "feat: 包含新文件的完整现场"

加入 -u 后,Stash Commit 对象会生成 第三个父节点,专门用来记录 Untracked 文件的内容。这样,当你 pop 回来时,新文件也会完好如初。

2. 包含被忽略文件 (-a)

这是极端的用法,请务必谨慎。

1
2
# -a 是 --all 的缩写
git stash push -a -m "force: 连 .gitignore 里的垃圾都带走"

高危警告-a 参数会将 .gitignore 中定义的忽略文件(如 node_modules/, target/, .log)全部打包进 Stash。这不仅会导致储藏对象体积爆炸,还会导致恢复速度极慢,甚至覆盖你现有的构建产物。除非你需要调试构建过程,否则 严禁使用

8.2.2. 交互式储藏:只带走你想要的

有时候,我们的工作区是非常“脏”的。

场景模拟:混合了调试代码的现场

你正在修复一个 Bug,为了定位问题,你在代码里加了 10 行 System.out.println("Debug...")。现在 Bug 修复了一半,你需要暂停工作。

如果直接 stash,等你恢复时,这 10 行垃圾日志也会跟着回来,还得手动删除。你希望:只储藏修复 Bug 的核心逻辑代码,丢弃那些临时的打印语句。

操作实战:Patch 模式

这需要用到我们在第五章学过的“交互式筛选”技巧。

1
2
# -p 是 --patch 的缩写
git stash push -p -m "fix: 只储藏核心逻辑"

执行后,Git 会进入交互模式,逐个展示工作区的修改块(Hunk),并询问你:

1
2
3
4
5
6
7
8
diff --git a/User.java b/User.java
index ...
--- a/User.java
+++ b/User.java
@@ -10,1 +10,1 @@
- int a = 1;
+ int a = 2;
Stash this hunk [y,n,q,a,d,/,e,?]?
  • 输入 y:将这段代码放入 Stash。
  • 输入 n:不储藏这段代码(保留在工作区,或者随后被重置掉)。
  • 输入 s:如果代码块太大,按 s 拆分成更小的块再选择。

通过这种方式,你可以生成一个极度纯净的 Stash 存档,就像做了一次完美的代码提交一样。

8.2.3. 灾难恢复:冲突突围通道

这是 Stash 最具工程价值,却最鲜为人知的功能。

场景模拟:时过境迁的冲突

  1. 你在一周前基于 master 分支 stash 了一份代码。
  2. 这一周内,master 分支已经被同事提交了几十次更新,部分代码结构发生了剧变。
  3. 今天你执行 git stash pop,试图恢复现场。

结果:Git 报出大量冲突(Conflict),文件里全是 <<<<<<<。你不得不一边看着一周前的逻辑,一边处理现在的代码,心态彻底崩了。

逃生方案:Stash Branch

此时,千万不要硬着头皮解决冲突。你应该使用 git stash branch

1
2
# 语法:git stash branch <新分支名> [stash 索引]
git stash branch recover-feature stash@{0}

底层逻辑

这个命令会执行一套非常巧妙的组合拳:

  1. 时光倒流:Git 会创建一个名为 recover-feature 的新分支。
  2. 锚点重置关键点来了,这个新分支的起点并不是当前的 master,而是 你当初执行 stash 时的那个 Commit
  3. 应用现场:在这个“过去的时间点”上应用你的 Stash。

结果:因为应用环境和储藏时的环境完全一致,所以 100% 不会产生冲突

你可以在这个独立的新分支上从容地整理代码,整理完毕后,再通过标准的 Merge 或 Rebase 流程合并回 master,将“解决冲突”的压力转移到合并阶段,而不是恢复阶段。


8.2.4 本节小结

核心要点

  • 警惕丢失:默认 Stash 不包含未追踪文件,必须养成使用 -u 参数的习惯。
  • 拒绝垃圾:使用 -p 交互式模式,只储藏有价值的代码逻辑,隔离调试日志。
  • 逃离冲突:当 pop 引发复杂冲突时,使用 git stash branch 在历史节点开辟新分支,从源头规避合并问题。

速查代码

1
2
3
4
5
6
7
8
# 1. 包含新文件的储藏(推荐默认使用)
git stash push -u -m "feat: 包含 untracked 文件"

# 2. 交互式部分储藏
git stash push -p -m "fix: 仅储藏逻辑代码"

# 3. 冲突逃生通道(创建新分支应用储藏)
git stash branch <new-branch-name> stash@{0}

8.3. 工作树(Worktree):同时处理多个分支的优雅方案

8.3.1. 理解 Git Worktree

什么是 Worktree?

Git Worktree 是 Git 2.5 版本引入的功能,它允许你从同一个 Git 仓库创建多个工作目录。每个工作目录可以检出不同的分支,拥有独立的工作区和暂存区,但共享同一个 Git 仓库数据。

核心概念解析:

  1. 主工作树(Main Worktree)

    • 就是你平常使用的包含 .git 目录的工作区
    • 这是仓库的 “本体”,所有其他工作树都链接到这里
  2. 链接工作树(Linked Worktree)

    • 通过 git worktree add 创建的额外工作目录
    • 包含一个 .git 文件(不是目录),指向主工作树的 Git 数据
  3. 工作树的隔离性

    • 每个工作树有独立的工作区文件
    • 每个工作树有独立的 HEAD 指针
    • 每个工作树有独立的索引(暂存区)
    • 但所有工作树共享提交历史、分支、标签等仓库数据

工作原理图示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
传统 Git 工作方式:
repository/
├── .git/ # Git 仓库数据
│ ├── objects/ # 对象存储
│ ├── refs/ # 引用
│ ├── HEAD # 当前分支指针
│ └── index # 暂存区
└── working files # 工作区文件

使用 Worktree 后:
repository/ # 主工作树
├── .git/
│ ├── objects/ # 共享的对象存储
│ ├── refs/ # 共享的引用
│ ├── worktrees/ # 工作树元数据
│ │ └── feature-branch/
│ │ ├── HEAD # 独立的 HEAD
│ │ └── index # 独立的暂存区
│ ├── HEAD # 主工作树的 HEAD
│ └── index # 主工作树的暂存区
└── working files

repository-feature/ # 链接工作树
├── .git # 文本文件,指向主仓库
└── working files # 独立的工作文件

8.3.2. 为什么需要 Worktree?

场景 1:频繁的上下文切换

假设你正在开发一个新功能,突然需要修复紧急 bug:

1
2
3
4
5
6
# 传统方式的问题
- 需要 stash 当前修改
- 切换分支会改变工作区所有文件
- 依赖可能不同,需要重新安装
- IDE 需要重新索引
- 构建缓存失效

场景 2:对比不同版本

需要同时查看或运行不同版本的代码:

1
2
3
# 传统方式需要
- 不断切换分支
- 或者克隆多个仓库副本(浪费空间)

场景 3:长时间的并行开发

多个功能同时进行,需要频繁在它们之间切换。

8.3.3. Worktree 基础操作

让我们通过一个完整可复现的示例来学习 Worktree 的基本操作。

准备测试环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建一个测试目录
mkdir git-worktree-demo && cd git-worktree-demo

# 初始化案例创建一些提交
echo "# Worktree Demo " > README.md && echo " console.log('version 1.0');" > app.js
git add . && git commit -m "Initial commit"

# 创建几个分支模拟真实场景
git branch feature/new-ui
git branch hotfix/security-patch

# 在主分支上再做一个提交
echo "console.log('main branch update');" >> app.js && git commit -am "Update main branch"

查看当前工作树:

1
2
# 列出所有工作树
git worktree list

输出:

1
D:/my-first-project  50924a8 [master]

此时只有一个工作树(主工作树)。

添加第一个链接工作树:

1
2
# 为 feature/new-ui 分支创建新的工作树
git worktree add ./demo-new-ui feature/new-ui

输出:

1
2
Preparing worktree (checking out 'feature/new-ui')
HEAD is now at abc123 Initial commit

验证结果:

1
2
# 再次列出工作树
git worktree list

输出:

此时在我们的 my-first-project 内则新建了一份新的拷贝文件夹

1
2
D:/my-first-project              50924a8 [master]
D:/my-first-project/demo-new-ui d6e9b59 [feature/new-ui]

查看工作树结构:

1
2
3
# 查看新工作树的内容
ls ../demo-new-ui/
cat ../demo-new-ui/.git

.git 文件内容:

可以看到他的 git 内容只是对于我们主 git 仓库的 work tree 引用

1
gitdir: D:/my-first-project/.git/worktrees/demo-new-ui

8.3.4. Worktree 管理

查看详细信息:

移除工作树:

1
2
3
4
5
6
7
8
# 正常移除
git worktree remove ../demo-debug

# 如果工作树有未提交的修改,需要强制移除
git worktree remove --force ../demo-api-v2

# 清理已删除但未清除的工作树引用
git worktree prune

锁定和解锁工作树:

1
2
3
4
5
6
7
8
# 锁定工作树,防止意外删除
git worktree lock ../demo-new-ui

# 查看锁定状态
git worktree list --verbose

# 解锁
git worktree unlock ../demo-new-ui

8.3.5 本节小结

Git Worktree 提供了一种优雅的方式来同时处理多个分支,特别适合以下场景:

  1. 并行开发:同时进行多个功能开发
  2. 紧急修复:不打断当前工作快速切换到修复分支
  3. 版本对比:同时运行和比较不同版本
  4. 代码审查:在不影响当前工作的情况下检出他人代码

核心命令速查:

1
2
3
4
5
6
7
8
9
# 基础操作
git worktree add <path> <branch> # 添加工作树
git worktree list # 列出工作树
git worktree remove <path> # 移除工作树

# 进阶操作
git worktree add -b <new-branch> <path> # 创建新分支
git worktree lock/unlock <path> # 锁定/解锁
git worktree prune # 清理垃圾

通过合理使用 Worktree,你可以显著提升多任务处理的效率,避免频繁的分支切换和环境重建。

8.4. 本章总结:构建高效的上下文切换体系

经过本章的深入学习,我们掌握了 Git 中两种截然不同但相辅相成的上下文切换机制:StashWorktree。它们分别代表了 “时间维度” 和 “空间维度” 的解决方案,让我们能够优雅地应对开发中的各种中断场景。

8.4.1. 核心知识回顾

Stash:轻量级的时间冻结器

  1. 本质理解

    • Stash 不是临时内存,而是特殊的 Commit 对象
    • 通过多父节点结构精确记录工作区和暂存区状态
    • 遵循 LIFO(后进先出)的栈结构管理
  2. 关键能力

    • 快速保存和恢复工作现场
    • 支持包含未追踪文件(-u
    • 支持交互式部分储藏(-p
    • 提供冲突逃生通道(stash branch

Worktree:重量级的空间并行器

  1. 本质理解

    • 从同一个仓库创建多个独立的工作目录
    • 每个工作树有独立的工作区、暂存区和 HEAD
    • 所有工作树共享对象存储和引用
  2. 关键能力

    • 真正的并行开发环境
    • 避免分支切换导致的环境重建
    • 物理隔离,互不干扰
    • 适合长期并行任务

8.4.2. 场景选择决策树

在实际工作中,如何选择使用 Stash 还是 Worktree?以下是一个实用的决策指南:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
开始:需要切换上下文?

├─ 中断时间 < 30分钟?
│ ├─ 是 → 使用 Stash
│ └─ 否 → 继续判断

├─ 需要同时运行多个版本?
│ ├─ 是 → 使用 Worktree
│ └─ 否 → 继续判断

├─ 项目有复杂的构建/依赖?
│ ├─ 是 → 使用 Worktree(避免重复构建)
│ └─ 否 → 使用 Stash

└─ 磁盘空间充足?
├─ 是 → 优先 Worktree
└─ 否 → 使用 Stash

8.4.3. 最佳实践对照表

维度StashWorktree
适用场景临时中断、快速切换长期并行开发、环境隔离
切换速度极快(秒级)较慢(需要创建目录)
空间占用几乎为零需要完整工作区副本
环境保持需要重建(依赖、缓存)完全独立,互不影响
冲突风险恢复时可能冲突合并时处理
管理复杂度简单(栈操作)需要管理多个目录

8.4.4. 工程化实践建议

1. Stash 工作流规范

1
2
3
4
5
6
7
8
9
10
11
12
# 标准化命名规范
git stash push -u -m "[类型]: 简短描述"
# 类型:feat/fix/docs/refactor/test/chore

# 定期清理(建议每周执行)
git stash list | wc -l # 检查数量
git stash clear # 慎用!清理所有

# 安全恢复流程
git stash show -p stash@{0} # 先预览
git stash apply stash@{0} # 再应用
git stash drop stash@{0} # 确认后删除

2. Worktree 目录规范

1
2
3
4
5
6
7
8
9
10
project/
├── main/ # 主工作树(master/main 分支)
├── features/ # 功能开发工作树
│ ├── user-system/
│ └── payment-api/
├── hotfixes/ # 紧急修复工作树
│ └── security-patch/
└── releases/ # 发布准备工作树
├── v2.0-rc/
└── v2.1-dev/

3. 混合使用策略

在大型项目中,Stash 和 Worktree 往往配合使用:

1
2
3
4
5
6
7
8
9
# 场景:在 feature worktree 中开发时,需要临时验证想法
cd features/user-system/
git stash push -u -m "feat: 暂存用户模块进度"
# ... 验证其他想法 ...
git stash pop

# 场景:主 worktree 需要紧急修复
cd main/
git worktree add ../hotfixes/issue-123 -b hotfix/issue-123

8.4.5. 常见误区与陷阱

Stash 误区

  1. 误区:认为 Stash 是临时存储,可以无限堆积

    • 真相:过多的 Stash 会影响性能,建议保持在 10 个以内
  2. 误区:Pop 失败就意味着数据丢失

    • 真相:冲突时 Stash 仍然保留,可以用 stash branch 恢复
  3. 误区:只用默认的 git stash

    • 真相:不加 -u 会丢失新文件,不加 -m 难以管理

Worktree 误区

  1. 误区:Worktree 就是克隆多个仓库

    • 真相:Worktree 共享对象存储,比多仓库节省大量空间
  2. 误区:可以在不同 Worktree 检出同一分支

    • 真相:Git 会阻止这种操作,避免冲突
  3. 误区:删除 Worktree 目录就完事了

    • 真相:应使用 git worktree remove,否则会留下垃圾引用

8.4.6. 速查命令卡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# ==== ==== == Stash 核心命令 == ==== ====
# 保存现场(推荐始终使用 -u -m)
git stash push -u -m "描述信息"

# 查看储藏
git stash list # 列表
git stash show -p stash@{n} # 详情

# 恢复现场
git stash pop # 应用并删除栈顶
git stash apply stash@{n} # 应用但保留
git stash branch <name> # 冲突逃生

# 管理储藏
git stash drop stash@{n} # 删除指定
git stash clear # 清空所有

# ==== ==== == Worktree 核心命令 == ==== ====
# 创建工作树
git worktree add <path> <branch> # 已有分支
git worktree add -b <branch> <path> # 新建分支
git worktree add <path> <commit-ish> # 特定提交

# 管理工作树
git worktree list # 列表
git worktree remove <path> # 删除
git worktree prune # 清理

# 高级操作
git worktree lock <path> # 锁定保护
git worktree unlock <path> # 解锁
git worktree move <path> <new> # 移动位置