1. 简介
Git 的 stash
功能允许我们在不提交变更的前提下临时保存工作状态,这在切换分支时特别有用。虽然我们可以一次性暂存所有修改(包括已暂存和未暂存的文件),但 Git 也支持仅暂存部分文件或部分内容。
本文将介绍几种在 Git 中选择性暂存变更的方法。我们将从创建一个示例仓库开始,演示如何使用命令行选择性地暂存特定文件、通过文件指定路径、暂存未追踪文件,以及使用交互式补丁方式暂存部分变更内容。
本文所有示例均在 Debian 12(Bookworm)与 GNU Bash 5.2.15 下测试通过,适用于大多数 POSIX 兼容环境。
2. 示例仓库
我们先创建一些修改,以便后续演示使用:
$ echo "$(date)" >> trackedfile
$ touch untrackedfile
$ rm deletedfile
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: deletedfile
modified: trackedfile
Untracked files:
(use "git add <file>..." to include in what will be committed)
untrackedfile
no changes added to commit (use "git add" and/or "git commit -a")
✅ 假设我们在每个章节开始前都会恢复到这个状态。
3. 使用 push
指定文件暂存
Git 提供了 git stash push
命令用于创建暂存条目。默认情况下,git stash
等价于 git stash push
。
从 Git 2.x 开始,push
支持直接传入文件路径参数,仅暂存这些文件的修改:
$ git stash [push] [--] <FILE_PATH_1> <FILE_PATH_2> ... <FILE_PATH_N>
📌 注意:push
和 --
是可选的,但建议保留以避免语法歧义。
示例:仅暂存 deletedfile
$ git stash -- deletedfile
Saved working directory and index state WIP on master: 9d7f6b2 last commit message
查看当前暂存列表:
$ git stash list
stash@{0}: WIP on master: 9d7f6b2 last commit message
查看暂存内容:
$ git stash show 0
deletedfile | 0
1 file changed, 0 insertions(+), 0 deletions(-)
仅 deletedfile
被暂存,其他修改仍然保留在工作区:
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: trackedfile
Untracked files:
(use "git add <file>..." to include in what will be committed)
untrackedfile
no changes added to commit (use "git add" and/or "git commit -a")
💡 小技巧:若要暂存多个文件,只需在命令后按空格列出即可。
4. 通过文件指定暂存路径
除了在命令行中列出文件路径,我们也可以通过一个文件来指定要暂存的文件列表,使用 --pathspec-from-file
参数:
$ git stash --pathspec-from-file=<LIST_FILE_PATH>
例如,创建一个 filelist
文件包含两个路径:
$ cat filelist
trackedfile
deletedfile
$ git stash --pathspec-from-file=filelist
Saved working directory and index state WIP on master: 9d7f6b2 last commit message
查看暂存内容:
$ git stash show
deletedfile | 0
trackedfile | 1 +
2 files changed, 1 insertion(+)
📌 注意:如果文件名包含特殊字符,建议使用 --pathspec-file-nul
并以 NULL
分隔路径。
5. 暂存未追踪文件
默认情况下,即使指定路径,Git 也无法暂存未追踪文件。需要使用 --include-untracked
(或 -u
)标志:
$ git stash --include-untracked -- untrackedfile
Saved working directory and index state WIP on master: 9d7f6b2 last commit message
📌 该标志同样适用于 --pathspec-from-file
。
6. Git 补丁机制简介
Git 的许多命令都支持 --patch
(或 -p
)参数,它会将修改拆分为“补丁块(hunk)”,并提供交互式界面让我们选择哪些块要暂存:
$ git add --patch
diff --git a/deletedfile b/deletedfile
deleted file mode 100644
index e69de29..0000000
(1/1) Stage deletion [y,n,q,a,d,?]? y
diff --git a/trackedfile b/trackedfile
index e69de29..ee82e24 100644
--- a/trackedfile
+++ b/trackedfile
@@ -0,0 +1 @@
+Sun Feb 22 10:02:10 AM EST 2024
(1/1) Stage this hunk [y,n,q,a,d,e,?]? n
常用选项:
y
:暂存当前块n
:跳过当前块q
:跳过当前块及后续所有块a
:暂存当前块及该文件后续所有块d
:跳过当前块及该文件后续所有块s
:将当前块拆分为更小的块e
:手动编辑当前块?
:显示帮助
7. 使用补丁方式暂存修改
git stash
的 push
和 save
子命令也支持 --patch
,用于交互式选择要暂存的修改块:
$ git stash push --patch
diff --git a/deletedfile b/deletedfile
deleted file mode 100644
index e69de29..0000000
(1/1) Stash deletion [y,n,q,a,d,?]? y
diff --git a/trackedfile b/trackedfile
index e69de29..ee82e24 100644
--- a/trackedfile
+++ b/trackedfile
@@ -0,0 +1 @@
+Sun Feb 22 10:02:10 AM EST 2024
(1/1) Stash this hunk [y,n,q,a,d,e,?]? n
📌 注意:该操作是原子性的,即如果中途按 Ctrl+C
取消,不会有任何修改被暂存。
⚠️ --patch
会隐式启用 --keep-index
,即暂存已暂存的变更,但不会影响暂存区。如需清除暂存区,应使用 --no-keep-index
。
如需暂存未追踪文件,仍需加上 --include-untracked
。
8. 总结
方法 | 是否支持未追踪文件 | 是否支持交互式选择 | 是否支持路径文件 |
---|---|---|---|
git stash -- <file> |
❌ | ❌ | ❌ |
--pathspec-from-file |
✅(需 -u ) |
❌ | ✅ |
--patch |
✅(需 -u ) |
✅ | ❌ |
Git 的 stash 功能非常强大,尤其是在我们需要保存部分工作状态时。通过本文介绍的几种方式,你可以更精细地控制哪些修改被暂存、哪些保留,从而提升工作效率。