简体中文 ▾ 主题 ▾ 最新版本 ▾ git-stash 最后更新于 2.52.0

名称

git-stash - 储藏脏工作目录中的修改

概要

git stash list [<log-options>]
git stash show [-u | --include-untracked | --only-untracked] [<diff-options>] [<stash>]
git stash drop [-q | --quiet] [<stash>]
git stash pop [--index] [-q | --quiet] [<stash>]
git stash apply [--index] [-q | --quiet] [<stash>]
git stash branch <branchname> [<stash>]
git stash [push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
	     [-u | --include-untracked] [-a | --all] [(-m | --message) <message>]
	     [--pathspec-from-file=<file> [--pathspec-file-nul]]
	     [--] [<pathspec>…​]]
git stash save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-q | --quiet]
           [-u | --include-untracked] [-a | --all] [<message>]
git stash clear
git stash create [<message>]
git stash store [(-m | --message) <message>] [-q | --quiet] <commit>
git stash export (--print | --to-ref <ref>) [<stash>…​]
git stash import <commit>

描述

当你想记录工作目录和索引的当前状态,但又想回到一个干净的工作目录时,请使用 git stash。该命令会将您的本地修改保存起来,并使工作目录还原到与 HEAD 提交相匹配的状态。

通过此命令储藏的修改可以用 git stash list 列出,用 git stash show 检查,并用 git stash apply 恢复(可能在不同的提交之上)。不带任何参数调用 git stash 相当于 git stash push。默认情况下,储藏条目会列为 "WIP on <branchname> …​",但在创建储藏时,您可以在命令行中提供更具描述性的消息。

您创建的最新储藏存储在 refs/stash 中;较旧的储藏可以在此引用的 reflog 中找到,并可以使用常规的 reflog 语法进行命名(例如 stash@{0} 是最近创建的储藏,stash@{1} 是它之前的储藏,也可以使用 stash@{2.hours.ago})。储藏也可以通过仅指定储藏索引来引用(例如,整数 <n> 等同于 stash@{<n>})。

命令

push [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-u | --include-untracked] [ -a | --all] [-q | --quiet] [(-m|--message) <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<路径规范>...]

将您的本地修改保存到一个新的 储藏条目(stash entry) 中,并将它们回滚到 HEAD(在工作树和索引中)。<message> 部分是可选的,用于提供储藏状态的说明。

为了快速制作快照,您可以省略 "push"。在这种模式下,不允许使用非选项参数,以防止拼写错误的子命令创建意外的储藏条目。此规则有两个例外:stash -p(作为 stash push -p 的别名)以及路径规范元素(为了消除歧义,允许在双横线 -- 后使用)。

save [-p | --patch] [-S | --staged] [-k | --[no-]keep-index] [-u | --include-untracked] [-a | --all] [-q | --quiet] [<消息>]

此选项已弃用,建议使用 git stash push。它与 "stash push" 的区别在于它不能接收路径规范。相反,所有非选项参数都会被连接起来形成储藏消息。

list [<日志选项>]

列出您当前拥有的储藏条目。每个 储藏条目 都会列出其名称(例如 stash@{0} 是最新条目,stash@{1} 是前一个,依此类推)、创建条目时所在的当前分支名称,以及该条目所基于的提交的简短描述。

stash@{0}: WIP on submit: 6ebd0e2... Update git-stash documentation
stash@{1}: On master: 9cc0589... Add git-stash

该命令接受适用于 git log 命令的选项,以控制显示内容和方式。参见 git-log[1]

show [-u | --include-untracked | --only-untracked] [<diff 选项>] [<储藏>]

以 diff 形式显示储藏条目中记录的更改,对比储藏内容与最初创建储藏条目时的提交。默认情况下,该命令显示 diffstat,但它接受 git diff 支持的任何格式(例如,使用 git stash show -p stash@{1} 以补丁形式查看倒数第二个条目)。如果没有提供 <diff-option>,默认行为将由 stash.showStatstash.showPatch 配置变量决定。您还可以使用 stash.showIncludeUntracked 来设置是否默认启用 --include-untracked

pop [--index] [-q | --quiet] [<储藏>]

从储藏列表中移除单个储藏状态并将其应用到当前工作树状态之上,即执行 git stash push 的反向操作。工作目录必须与索引匹配。

应用状态可能会因冲突而失败;在这种情况下,它不会从储藏列表中移除。您需要手动解决冲突,然后手动调用 git stash drop

apply [--index] [-q | --quiet] [<储藏>]

类似于 pop,但不从储藏列表中移除状态。与 pop 不同,<stash> 可以是任何看起来像是由 stash pushstash create 创建的提交。

branch <分支名> [<储藏>]

创建一个名为 <branchname> 的新分支并检出,起点是最初创建 <stash> 时的提交,将 <stash> 中记录的更改应用到新的工作树和索引中。如果操作成功,且 <stash>stash@{<修订版本>} 形式的引用,则它会丢弃该 <stash>

这在以下情况非常有用:如果您运行 git stash push 的分支发生了很大变化,导致 git stash apply 因冲突而失败。由于储藏条目是应用在运行 git stash 时作为 HEAD 的提交之上,因此它会无冲突地恢复最初储藏的状态。

clear

移除所有储藏条目。请注意,这些条目随后将面临修剪(pruning),可能无法恢复(参见下文 示例 中的可能策略)。

drop [-q | --quiet] [<储藏>]

从储藏条目列表中移除单个储藏条目。

create

创建一个储藏条目(这是一个常规的提交对象)并返回其对象名称,而不将其存储在引用命名空间的任何位置。这旨在用于脚本。它可能不是您想要使用的命令;请参阅上面的 "push"。

store

将在储藏引用中通过 git stash create 创建的给定储藏(这是一个悬空的合并提交)进行存储,并更新储藏的 reflog。这旨在用于脚本。它可能不是您想要使用的命令;请参阅上面的 "push"。

export ( --print | --to-ref <引用> ) [<储藏>...]

将指定的储藏(如果未指定则为全部)导出为提交链,可以使用正常的 fetch 和 push 机制进行传输,然后使用 import 子命令导入。

import <提交>

从指定的提交(必须由 export 创建)导入指定的储藏,并将其添加到储藏列表中。要替换现有的储藏,请先使用 clear

选项

-a
--all

此选项仅对 pushsave 命令有效。

所有被忽略和未跟踪的文件也会被储藏,然后通过 git clean 清理掉。

-u
--include-untracked
--no-include-untracked

pushsave 命令一起使用时,所有未跟踪的文件也会被储藏,然后通过 git clean 清理掉。

show 命令一起使用时,将储藏条目中未跟踪的文件作为 diff 的一部分显示。

--only-untracked

此选项仅对 show 命令有效。

在 diff 中仅显示储藏条目中的未跟踪文件。

--index

此选项仅对 popapply 命令有效。

尝试不仅恢复工作树的更改,还恢复索引的更改。但是,如果您有冲突(冲突存储在索引中,因此您无法再按原样应用更改),这可能会失败。

-k
--keep-index
--no-keep-index

此选项仅对 pushsave 命令有效。

所有已添加到索引中的更改都保持不变。

-p
--patch

此选项仅对 pushsave 命令有效。

从 HEAD 与工作树之间的 diff 中以交互方式选择要储藏的数据块(hunks)。储藏条目的构建方式使其索引状态与仓库的索引状态相同,其工作树仅包含您交互式选择的更改。所选更改随后会从您的工作树中回滚。请参阅 git-add[1] 的 “交互模式” 部分,了解如何操作 --patch 模式。

--patch 选项隐含了 --keep-index。您可以使用 --no-keep-index 来覆盖此行为。

-U<n>
--unified=<n>

生成具有 <n> 行上下文的 diff。如果配置选项未设置,则默认为 diff.context 或 3。

--inter-hunk-context=<n>

在差异块之间显示上下文,最多达指定行数 <number>,从而合并彼此接近的块。默认为 diff.interHunkContext,如果未设置配置选项则为 0。

-S
--staged

此选项仅对 pushsave 命令有效。

仅储藏当前暂存的更改。这类似于基本的 git commit,不同之处在于状态是提交到储藏中而不是当前分支。

--patch 选项的优先级高于此选项。

--pathspec-from-file=<文件>

此选项仅对 push 命令有效。

路径规范(Pathspec)在 <file> 中传递,而不是通过命令行参数。如果 <file> 恰好是 -,则使用标准输入。路径规范元素由 LF 或 CR/LF 分隔。路径规范元素可以按照配置变量 core.quotePath 的解释进行引用(参见 git-config[1])。另请参见 --pathspec-file-nul 和全局 --literal-pathspecs

--pathspec-file-nul

此选项仅对 push 命令有效。

仅在与 --pathspec-from-file 配合使用时有意义。路径规范元素由 NUL 字符分隔,所有其他字符都按原义理解(包括换行符和引号)。

-q
--quiet

此选项仅对 applydroppoppushsavestore 命令有效。

安静模式,抑制反馈消息。

--print

此选项仅对 export 命令有效。

创建代表导出储藏的提交链,不将其存储在引用命名空间的任何位置,并将对象 ID 打印到标准输出。这是为脚本设计的。

--to-ref

此选项仅对 export 命令有效。

创建代表导出储藏的提交链,并将其存储到指定的引用中。

--

此选项仅对 push 命令有效。

将路径规范与选项分隔开,以消除歧义。

<路径规范>...

此选项仅对 push 命令有效。

新的储藏条目仅记录与路径规范匹配的文件的修改状态。随后,索引条目和工作树文件也仅针对这些文件回滚到 HEAD 中的状态,而不匹配路径规范的文件则保持不变。

有关更多详细信息,请参阅 gitglossary[7] 中的 pathspec 条目。

<储藏>

此选项仅对 applybranchdroppopshowexport 命令有效。

stash@{<修订版本>} 形式的引用。如果没有给出 <stash>,则假定为最新的储藏(即 stash@{0})。

讨论

储藏条目表示为一个提交,其树记录了工作目录的状态,其第一个父提交是创建该条目时的 HEAD。第二个父提交的树记录了创建该条目时索引的状态,并且它被设置为 HEAD 提交的子节点。家谱图如下所示

       .----W
      /    /
-----H----I

其中 HHEAD 提交,I 是记录索引状态的提交,W 是记录工作树状态的提交。

示例

拉取到脏树

当您正忙于某事时,您得知上游有变更可能与您正在做的事情相关。当您的本地更改与上游更改不冲突时,简单的 git pull 就能让您继续前进。

但是,在某些情况下,您的本地更改确实与上游更改冲突,而 git pull 拒绝覆盖您的更改。在这种情况下,您可以将您的更改储藏起来,执行拉取,然后取消储藏,如下所示

$ git pull
 ...
file foobar not up to date, cannot merge.
$ git stash
$ git pull
$ git stash pop
中断的工作流

当您正忙于某事时,您的老板进来要求您立即修复某些问题。传统上,您会提交到一个临时分支以保存您的更改,然后返回到原始分支进行紧急修复,如下所示

# ... hack hack hack ...
$ git switch -c my_wip
$ git commit -a -m "WIP"
$ git switch master
$ edit emergency fix
$ git commit -a -m "Fix in a hurry"
$ git switch my_wip
$ git reset --soft HEAD^
# ... continue hacking ...

您可以使用 git stash 来简化上述过程,如下所示

# ... hack hack hack ...
$ git stash
$ edit emergency fix
$ git commit -a -m "Fix in a hurry"
$ git stash pop
# ... continue hacking ...
测试部分提交

当您想从工作树中的更改中进行两次或更多次提交,并且想在提交前测试每个更改时,可以使用 git stash push --keep-index

# ... hack hack hack ...
$ git add --patch foo            # add just first part to the index
$ git stash push --keep-index    # save all other changes to the stash
$ edit/build/test first part
$ git commit -m 'First part'     # commit fully tested change
$ git stash pop                  # prepare to work on all other changes
# ... repeat above five steps until one commit remains ...
$ edit/build/test remaining parts
$ git commit foo -m 'Remaining parts'
保存无关更改供将来使用

当您正在进行大规模更改,并发现了一些不想忘记修复的无关问题时,您可以进行更改、将其暂存,然后使用 git stash push --staged 将其储藏起来供将来使用。这类似于提交暂存的更改,只是该提交最终位于储藏中,而不是当前分支上。

# ... hack hack hack ...
$ git add --patch foo           # add unrelated changes to the index
$ git stash push --staged       # save these changes to the stash
# ... hack hack hack, finish current changes ...
$ git commit -m 'Massive'       # commit fully tested changes
$ git switch fixup-branch       # switch to another branch
$ git stash pop                 # to finish work on the saved changes
恢复被错误清除/丢弃的储藏条目

如果您误删或清除了储藏条目,它们无法通过正常的安全机制恢复。但是,您可以尝试以下咒语来获取仍在存储库中但已无法访问的储藏条目列表

git fsck --unreachable |
grep commit | cut -d\  -f3 |
xargs git log --merges --no-walk --grep=WIP

配置

本节中以下所有内容均从 git-config[1] 文档中选择性地包含。内容与彼处相同:

stash.index

如果设置为 true,git stash applygit stash pop 的行为将如同提供了 --index 选项一样。默认为 false。

stash.showIncludeUntracked

如果设置为 true,git stash show 命令将显示储藏条目中未跟踪的文件。默认为 false。

stash.showPatch

如果设置为 true,不带选项的 git stash show 命令将以补丁形式显示储藏条目。默认为 false。

stash.showStat

如果设置为 true,不带选项的 git stash show 命令将显示储藏条目的 diffstat。默认为 true。

GIT

Git[1] 套件的一部分