Git Stash 用法详解:临时保存工作区修改 – wiki基地


Git Stash 用法详解:临时保存工作区修改

在软件开发的日常工作中,我们常常会遇到这样的情况:正在处理一个新功能或修复一个 Bug,突然间需要切换到另一个分支去处理一个紧急的 Bug,或者需要拉取远程仓库的最新代码。然而,当前的工作区可能存在大量尚未完成、无法提交的修改。如果直接切换分支,这些修改可能会导致冲突,甚至丢失;如果强行提交一个“半成品”的修改,又会污染提交历史,给团队协作带来麻烦。

这时,Git Stash 就如同一位救星,为我们提供了完美的解决方案。它允许我们临时保存当前工作区和暂存区的所有修改,将工作区恢复到干净的状态,以便我们进行其他操作。完成其他任务后,我们再随时恢复之前保存的修改,继续原来的工作。

本文将深入探讨 Git Stash 的各个方面,从基本概念到高级用法,再到实际应用场景和注意事项,助您成为 Git Stash 的使用高手。


一、 Git Stash 简介:为什么需要它?

Git Stash 的核心功能是临时存储未提交的本地修改。它将你的工作目录和暂存区中的所有更改(包括已跟踪文件的修改和新添加的文件到暂存区的更改)打包成一个临时的“存储单元”,并将其推入一个栈中。这个栈是与你的本地仓库关联的,但它不是 Git 提交历史的一部分。

Git Stash 解决的核心问题:

  1. 快速切换上下文: 当你需要立即切换到另一个分支(例如修复高优先级 Bug)时,但当前分支的工作尚未完成,无法提交。
  2. 拉取远程更新: 当你的本地分支有未提交的修改,而你想拉取远程仓库的最新代码时,这些修改可能会与远程更新冲突。Stash 允许你先保存本地修改,拉取更新,再恢复修改。
  3. 清理工作区: 当你在进行一些实验性改动,或者仅仅想暂时清空工作区以便查看某个历史版本时,Stash 可以帮助你快速实现。
  4. 避免不完整的提交: 避免为了切换分支而被迫提交一些不完整的、未经测试的代码。

Stash 不会做什么?

  • 不会保存未跟踪的文件(Untracked Files): 默认情况下,Git Stash 不会存储那些从未被 git add 过的文件。
  • 不会保存被忽略的文件(Ignored Files): 同样,被 .gitignore 规则忽略的文件也不会被 Stash。

二、 Git Stash 的核心命令详解

理解 Git Stash 的各个命令是熟练使用的基础。我们将逐一介绍:

2.1 git stashgit stash save [message]:保存当前修改

这是最常用、最基本的命令,用于将当前工作区和暂存区的修改保存起来。

  • git stash 这是最简洁的用法。Git 会自动生成一个默认的描述信息(例如 “WIP on master: 0a1b2c3 Initial commit”),其中 “WIP” 表示 “Work In Progress”,后面是当前分支名和最新一次提交的哈希值与描述。
  • git stash save "Your custom message" 推荐使用此形式。你可以为你的 stash 添加一个有意义的描述信息,这在你有多个 stash 并且需要区分它们时非常有用。消息会出现在 git stash list 的输出中。

保存的内容:

  1. 已修改但未暂存的文件(Modified but not staged)。
  2. 已暂存的文件(Staged files)。

示例:

“`bash

假设你在 feature 分支,对 file1.txt 进行了修改,并对 file2.txt 进行了修改并已暂存。

git status

On branch feature

Changes to be committed:

(use “git restore –staged …” to unstage)

modified: file2.txt

Changes not staged for commit:

(use “git add …” to update what will be committed)

(use “git restore …” to discard changes in working directory)

modified: file1.txt

保存这些修改,并添加描述

git stash save “WIP: Implemented login functionality”

再次查看状态,工作区和暂存区都已干净

git status

On branch feature

nothing to commit, working tree clean

“`

此时,你的修改已经被成功保存到 stash 栈中,并且工作区和暂存区都变得干净,你可以放心地切换分支或执行其他操作。

2.2 git stash list:查看所有保存的修改

此命令用于列出所有已保存的 stash。

输出格式:

stash@{0}: On master: Initial commit
stash@{1}: On feature: Added new user registration page
stash@{2}: WIP on feature: 0a1b2c3 Implemented login functionality

  • stash@{n}n 是一个整数,表示 stash 在栈中的位置。stash@{0} 总是代表最新保存的 stash。
  • On <branch_name>:表示该 stash 是在哪个分支上创建的。
  • message:你保存时提供的描述信息,或者 Git 自动生成的默认信息。

示例:

“`bash
git stash list

stash@{0}: WIP on feature: 0a1b2c3 Implemented login functionality

stash@{1}: On master: Urgent bug fix

``
这个输出告诉我们,我们目前有两个 stash,最新的一个 (
stash@{0}) 是在feature` 分支上创建的,描述是 “WIP: Implemented login functionality”。

2.3 git stash apply [stash@{n}]:应用保存的修改(不删除 stash)

apply 命令用于将指定的 stash 重新应用到当前工作区。

  • git stash apply 默认应用最新(即 stash@{0})的 stash。
  • git stash apply stash@{n} 应用指定的 stash。

特点:

  • 应用 stash 后,该 stash 仍然保留在 stash 列表中。这意味着你可以多次应用同一个 stash,或者在不同的分支上应用它(尽管这通常不推荐,因为它可能导致冲突)。
  • 不会自动恢复暂存区状态: apply 命令会将 stash 中的所有修改都应用到工作区,但不会自动将它们重新添加到暂存区。你需要手动 git add 来暂存它们。

冲突处理:
如果应用的 stash 与当前工作区存在冲突,Git 会在冲突文件中标记出冲突区域,你需要手动解决冲突,然后 git add 解决后的文件,最后 git commit

示例:

“`bash

假设我们之前保存了一个 stash

git stash list

stash@{0}: WIP on feature: 0a1b2c3 Implemented login functionality

切换到 feature 分支

git checkout feature

应用最新保存的 stash

git stash apply

此时 git status 会显示之前保存的修改,但它们都处于未暂存状态

git status

On branch feature

Changes not staged for commit:

(use “git add …” to update what will be committed)

(use “git restore …” to discard changes in working directory)

modified: file1.txt

modified: file2.txt (Note: this was staged before, but apply makes it unstaged)

再次查看 stash 列表,stash 仍然存在

git stash list

stash@{0}: WIP on feature: 0a1b2c3 Implemented login functionality

“`

2.4 git stash pop [stash@{n}]:应用保存的修改并删除 stash

pop 命令的功能与 apply 类似,但它在成功应用 stash 后,会自动从 stash 列表中删除该 stash

  • git stash pop 默认弹出并应用最新(即 stash@{0})的 stash。
  • git stash pop stash@{n} 弹出并应用指定的 stash。

特点:

  • 这是最常用的恢复 stash 的方式,因为它在使用后会保持 stash 列表的整洁。
  • apply 相同,它会将 stash 中的所有修改都应用到工作区,但不会自动将它们重新添加到暂存区。

冲突处理:
如果 pop 过程中发生冲突,Git 会提示你解决冲突。解决冲突后,stash 不会被自动删除。你需要手动解决冲突,git addgit commit,然后根据需要手动 git stash drop 掉那个冲突的 stash。

示例:

“`bash

假设我们有一个 stash

git stash list

stash@{0}: WIP on feature: 0a1b2c3 Implemented login functionality

弹出并应用最新 stash

git stash pop

此时 git status 会显示之前保存的修改

git status

On branch feature

Changes not staged for commit:

(use “git add …” to update what will be committed)

(use “git restore …” to discard changes in working directory)

modified: file1.txt

modified: file2.txt

再次查看 stash 列表,stash 已被删除

git stash list

(没有输出,表示 stash 列表为空)

“`

2.5 git stash drop [stash@{n}]:删除一个保存的修改

此命令用于从 stash 列表中删除指定的 stash。

  • git stash drop 默认删除最新(即 stash@{0})的 stash。
  • git stash drop stash@{n} 删除指定的 stash。

注意: 删除操作是不可逆的。一旦删除,该 stash 中的修改就找不回来了。

示例:

“`bash
git stash list

stash@{0}: WIP on feature: 0a1b2c3 Implemented login functionality

stash@{1}: On master: Urgent bug fix

删除最新的 stash

git stash drop

Dropped stash@{0} (WIP on feature: 0a1b2c3 Implemented login functionality).

git stash list

stash@{0}: On master: Urgent bug fix

删除 stash@{0} (现在它代表了原来的 stash@{1})

git stash drop stash@{0}

Dropped stash@{0} (On master: Urgent bug fix).

git stash list

(没有输出,表示 stash 列表为空)

“`

2.6 git stash clear:删除所有保存的修改

此命令用于清空整个 stash 列表,删除所有已保存的 stash。

极其危险的操作! 请务必在确认所有 stash 都已不再需要时才使用此命令。

示例:

“`bash
git stash list

stash@{0}: WIP on feature: 0a1b2c3 Implemented login functionality

stash@{1}: On master: Urgent bug fix

git stash clear

(没有任何输出,但所有 stash 都被删除了)

git stash list

(没有输出,表示 stash 列表为空)

“`


三、 Git Stash 的高级用法

除了上述核心命令,Git Stash 还提供了一些高级选项,使其功能更加强大和灵活。

3.1 git stash show [stash@{n}] [-p]:查看 stash 的内容

这个命令允许你查看一个 stash 到底包含了哪些修改,而无需将其应用到工作区。

  • git stash show 默认显示最新 stash (stash@{0}) 的简要差异统计信息(哪些文件被修改,以及增删行数)。
  • git stash show stash@{n} 显示指定 stash 的简要差异。
  • git stash show -pgit stash show --patch 显示最新 stash (stash@{0}) 的完整差异(像 git diff 一样显示具体修改内容)。
  • git stash show -p stash@{n} 显示指定 stash 的完整差异。

示例:

“`bash

查看最新 stash 的简要统计

git stash show

file1.txt | 2 +-

file2.txt | 3 ++-

2 files changed, 3 insertions(+), 2 deletions(-)

查看最新 stash 的完整差异

git stash show -p

diff –git a/file1.txt b/file1.txt

index a1b2c3d..e4f5g6h 100644

— a/file1.txt

+++ b/file1.txt

@@ -1,3 +1,4 @@

Line 1

-Line 2 old

+Line 2 new content

Line 3

+New line 4

查看 stash@{1} 的完整差异

git stash show -p stash@{1}
“`

3.2 git stash -ugit stash --include-untracked:保存未跟踪文件

默认情况下,git stash 不会保存那些从未被 git add 过的“未跟踪文件”。如果你想将它们也一并保存,可以使用 -u--include-untracked 选项。

示例:

“`bash

假设有一个 untracked_file.txt 和 file1.txt 的修改

git status

On branch feature

Changes not staged for commit:

(use “git add …” to update what will be committed)

(use “git restore …” to discard changes in working directory)

modified: file1.txt

Untracked files:

(use “git add …” to include in what will be committed)

untracked_file.txt

git stash -u save “Including untracked file”

git status

On branch feature

nothing to commit, working tree clean

此时 untracked_file.txt 也会被 stash

``
当你
applypop这个 stash 时,untracked_file.txt` 将会重新出现在你的工作区。

3.3 git stash -agit stash --all:保存所有文件(包括被忽略文件)

这个选项比 -u 更进一步,它会保存所有文件,包括那些被 .gitignore 规则忽略的文件。这在你需要彻底清空工作区,甚至包括那些通常被忽略的编译产物或日志文件时非常有用。

注意: 谨慎使用此选项,因为它可能会保存一些你本不希望被 Git 触及的文件。

示例:

“`bash

假设有 .log 文件被 .gitignore 忽略,以及未跟踪文件和修改过的文件

git stash -a save “Stashing everything including ignored”

此时工作区会变得非常干净

“`

3.4 git stash branch <new_branch_name> [stash@{n}]:从 stash 创建新分支

如果你发现某个 stash 实际上代表了一个可以独立开发的新功能或 Bug 修复,你可以直接从这个 stash 创建一个新的分支。这个命令会创建一个新的分支,将 stash 中的修改应用到该分支,然后删除该 stash。

工作流程:

  1. 创建一个新分支,并切换到该分支。
  2. 将指定的 stash(或最新的 stash)应用到这个新分支。
  3. 如果应用成功,则删除该 stash。

示例:

“`bash
git stash list

stash@{0}: WIP on feature: 0a1b2c3 Implemented login functionality

stash@{1}: On master: Urgent bug fix

从最新的 stash 创建一个名为 “feature-login” 的新分支

git stash branch feature-login

此时,你会在 “feature-login” 分支,并且之前 stash@{0} 的修改已经应用到了这个分支,

并且该 stash 已经被删除。

git status

On branch feature-login

Changes not staged for commit:

(use “git add …” to update what will be committed)

(use “git restore …” to discard changes in working directory)

modified: file1.txt

modified: file2.txt

git stash list

stash@{0}: On master: Urgent bug fix (原来的 stash@{1} 现在变成了 stash@{0})

“`

3.5 局部 Stash (git stash push -p / --patchgit stash --keep-index)

虽然 git stash 默认会保存所有修改,但有时你只想保存工作区的一部分修改,而保留另一部分或已暂存的修改。这可以通过结合 Git 的其他功能来实现。

  • 交互式局部暂存 (git add -p) + git stash --keep-index

    1. 使用 git add -p 交互式地将你想要保留在工作区或提交的修改添加到暂存区。
    2. 然后使用 git stash --keep-index。这个命令会保存所有未暂存的修改,而保留已暂存的修改不动。
  • 交互式局部 Stash (git stash push -pgit stash --patch):
    这个命令允许你像 git add -p 一样,交互式地选择哪些修改要被 stash 起来,哪些要保留在工作区。

    示例:

    “`bash

    假设 file1.txt 和 file2.txt 都有修改

    git status

    modified: file1.txt

    modified: file2.txt

    交互式选择要 stash 的修改

    git stash push –patch

    会逐个提示你 file1.txt 和 file2.txt 中的每个 hunk (修改块) 是否要 stash

    输入 ‘y’ 或 ‘n’ 来决定

    Stashed changes to file1.txt

    Stashed changes to file2.txt

    Saved working directory and index state WIP on master: …

    “`
    这个方法非常灵活,可以精确控制哪些修改被 stash。


四、 Git Stash 的实际应用场景

理解了命令,我们来看看 Git Stash 在实际开发中的常见应用场景。

4.1 情景一:紧急 Bug 修复 (Hotfix)

这是 git stash 最经典的用例。

  • 问题: 你正在 feature-X 分支上开发新功能,代码改动很多,尚未达到可提交的状态。突然,线上出现一个紧急 Bug,需要立即在 masterdevelop 分支上进行修复。
  • 解决方案:
    1. feature-X 分支上,运行 git stash save "WIP for feature X",临时保存你的工作。
    2. 切换到 masterdevelop 分支:git checkout master
    3. 创建并切换到 Bug 修复分支:git checkout -b hotfix/critical-bug
    4. 修复 Bug,提交代码,并推送到远程仓库。
    5. 切换回 feature-X 分支:git checkout feature-X
    6. 恢复之前保存的工作:git stash pop
    7. 继续开发 feature-X

4.2 情景二:切换分支

当你需要查看或处理其他分支,但当前分支有未提交的修改时。

  • 问题: 你在 dev 分支工作,需要切换到 test 分支进行一些测试,但 dev 分支上的代码还没写完。
  • 解决方案:
    1. dev 分支上,git stash 保存当前修改。
    2. 切换到 test 分支:git checkout test
    3. 完成你在 test 分支上的操作。
    4. 切换回 dev 分支:git checkout dev
    5. 恢复之前保存的修改:git stash pop

4.3 情景三:拉取远程更新

当你的本地分支有修改,但需要先拉取远程仓库的最新代码时。

  • 问题: 你在 master 分支有本地修改,尝试 git pull 时被 Git 阻止,因为合并远程代码会与本地修改冲突。
  • 解决方案:
    1. git stash 保存本地修改。
    2. git pull 拉取远程最新代码。
    3. git stash pop 恢复本地修改。
    4. 如果出现冲突,解决冲突并提交。

4.4 情景四:清理工作区进行试验

当你只想临时清空工作区,进行一些实验性改动,或者仅仅想看看某个历史提交的状态。

  • 问题: 你想在一个干净的工作区下,基于某个旧的提交进行一些快速测试,或者想丢弃当前所有的本地修改,重新开始。
  • 解决方案:
    1. git stash 保存当前工作。
    2. 现在工作区是干净的,你可以 git checkout <commit_hash> 查看历史状态,或者开始新的实验。
    3. 实验结束后,如果你决定放弃之前的 stash,可以直接 git stash drop;如果你想恢复,就 git stash pop

4.5 情景五:处理代码审查或合并冲突

当你在一个复杂的合并操作中遇到冲突,或者在代码审查期间需要临时修改其他部分的代码。

  • 问题: 你正在进行一个特性分支到主分支的合并,遇到了大量的冲突。解决冲突需要时间,但你突然发现需要修改一个独立于合并冲突的 Bug。
  • 解决方案:
    1. 在解决冲突之前(或在解决了一部分冲突后),如果你需要暂停解决冲突并处理其他事情,可以先 git stash push "Merge conflict WIP"
    2. 此时,冲突状态会被保存,工作区会回到合并前的状态(或你在 stash 时已经解决了一部分的冲突会被保存)。
    3. 你可以切换分支去处理那个 Bug。
    4. 处理完 Bug 后,切换回原来的分支。
    5. git stash pop 恢复之前的合并冲突状态,继续解决冲突。

五、 Git Stash 的注意事项与最佳实践

Git Stash 虽好用,但仍有一些需要注意的地方和最佳实践,以避免潜在的问题。

5.1 Stash 不是提交,它是本地的、临时的

  • 不是提交: Stash 不是 Git 提交历史的一部分。这意味着它不会出现在 git log 中,也不会被 git push 到远程仓库。
  • 本地且临时: Stash 只存在于你本地的仓库中。如果你克隆了一个新的仓库副本,或者在另一台机器上工作,你是看不到这些 stash 的。因此,不要将 stash 作为长期保存重要工作的手段。重要的工作应该通过提交到 Git 仓库或创建分支来保存。

5.2 及时清理 Stash

  • 避免堆积: 随着时间的推移,stash 列表可能会变得很长,让你难以辨别每个 stash 的具体内容。
  • 使用描述信息: 在使用 git stash save "message" 时,务必添加有意义的描述信息,帮助你快速识别。
  • 完成后删除: 当你不再需要某个 stash 时,应及时使用 git stash dropgit stash pop 删除它,保持列表的整洁。

5.3 理解 applypop 的区别

  • apply 恢复但不删除 stash。当你可能需要多次应用同一个 stash,或者不确定是否彻底用完它时,可以选择 apply
  • pop 恢复并删除 stash。这是更常用的方式,因为它在使用后会保持 stash 列表的整洁。通常,当你确定这个 stash 只用一次且用完即弃时,使用 pop

5.4 冲突处理

  • 准备冲突: 当你 applypop 一个 stash 时,如果当前工作区与 stash 存在差异,可能会发生冲突。
  • 解决冲突: Git 会在冲突文件中标记出冲突区域,你需要手动编辑文件解决冲突。
  • 后续操作: 解决冲突后,需要 git add <resolved_file>,然后 git commit。如果使用的是 pop 并且发生了冲突,stash 不会自动删除,你需要手动 git stash drop 它。

5.5 谨慎使用 clear 命令

  • git stash clear 会无情地删除所有 stash,并且这个操作是不可逆的。
  • 请只在你百分之百确定所有 stash 都不再需要时才使用此命令。

5.6 处理未跟踪文件和被忽略文件

  • 默认 git stash 不会处理未跟踪文件和被忽略文件。
  • 如果需要将它们也包含在 stash 中,请使用 git stash -u (include untracked) 或 git stash -a (all files)。
  • 考虑这些文件的性质:对于编译产物、日志文件等,通常不应纳入版本控制,也不应随意 stash。

5.7 不是替代分支的方案

  • git stash 是一个方便的工具,用于临时保存未提交的修改,以便在同一分支或不同分支间切换上下文。
  • 但它绝不是替代 Git 分支的方案。如果你有长期、独立的功能开发或 Bug 修复任务,请始终使用 git branch 来创建新的分支。分支是 Git 管理不同开发线的最基本和最强大的机制。

六、 总结

Git Stash 是 Git 提供的一个极其便利且强大的工具,它使得开发者在处理日常工作流中的各种中断和切换时,能够保持工作区的整洁和效率。通过临时保存工作区和暂存区的修改,你可以在不提交不完整代码的前提下,自由地切换分支、拉取更新、修复紧急 Bug 或进行试验。

熟练掌握 git stash 的基本命令(save/pushlistapplypopdropclear)和高级用法(show-u-abranch--patch)是提升 Git 工作效率的关键。同时,遵循最佳实践,如及时清理 stash、添加有意义的描述信息、理解 applypop 的差异,以及正确处理冲突,将帮助你更好地驾驭这一工具。

记住,git stash 是你的临时避风港,但重要的、长期性的工作始终应该通过 git commitgit branch 来妥善管理。希望通过本文的详细介绍,您能对 Git Stash 有一个全面而深入的理解,并在日常开发中运用自如!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部