1. Git 暂存(Staging)

在 Git 的三大工作区域中,我们主要在工作区(Working Tree)中进行修改操作,例如:

$ echo more >> file1
$ cat <<EOI >file2
> new
> EOI
$ rm file3

执行完这些操作后,可以使用 git status 查看当前状态:

$ 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)
        modified:   file1
        deleted:    file3

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        file2

no changes added to commit (use "git add" and/or "git commit -a")

此时,我们看到的是“未暂存”的修改。我们可以选择是否将这些文件的修改加入暂存区(Staging Area):

$ git add file1
$ git add file2

此时,我们并没有暂存 file3 的删除操作,但将 file1file2 的新内容加入了暂存区。当然,我们仍然可以继续添加或撤销暂存。

确认无误后,再次查看状态:

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   file1
        new file:   file2

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:    file3

此时,Changes to be committed 下显示的是已暂存的内容。

当我们执行 git commit 后,Git 会将这些暂存内容提交为一个新的提交节点,并更新当前分支的指针。


2. Git 的“中间状态”问题

有时候,我们可能还没有完成当前的修改,但需要切换分支去做其他事情。比如:

$ git checkout branch1
error: Your local changes to the following files would be overwritten by checkout:
        file1
Please commit your changes or stash them before you switch branches.
Aborting

虽然 Git 不需要动 file3file2,但 file1 的当前状态与暂存或未暂存版本有冲突,Git 会阻止切换分支以防止数据丢失。

这是因为 Git 的暂存区和工作区是全局的,不与分支绑定。一旦切换分支,这些修改可能会被覆盖或丢失。


3. Git 储藏(Stash)

Git 提供了一个非常实用的功能:git stash,它允许我们保存当前的工作状态,而不必提交。之后可以随时恢复这些修改。

3.1. 准备环境

假设我们当前工作区是干净的:

$ git status
On branch master
nothing to commit, working tree clean

现在我们做一些修改:

$ touch untrackedfile
$ echo $(date) >> trackedfile
$ 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")

如果我们想暂时放下当前工作,去处理其他任务,可以使用 git stash

3.2. 创建储藏

$ git stash
Saved working directory and index state WIP on master: 3fa666d last commit message

此时再次查看状态:

$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        untrackedfile

nothing added to commit but untracked files present (use "git add" to track)

可以看到,只有未跟踪的文件 untrackedfile 还在,其他修改都被储藏了。

3.3. 储藏未跟踪文件

默认情况下,git stash 不会保存未跟踪的文件。如果需要包含它们,使用 --include-untracked(或 -u):

$ git stash --include-untracked
Saved working directory and index state WIP on master: 3fa666d last commit message
$ git status
On branch master
nothing to commit, working tree clean

也可以使用 --all(或 -a),它还会包括被 .gitignore 忽略的文件。

3.4. 查看储藏列表

Git 的 stash 是一个栈结构,可以用 git stash list 查看:

$ git stash list
stash@{0}: WIP on master: 3fa666d last commit message
stash@{1}: WIP on master: 3fa666d last commit message

每个 stash 有一个编号,可以用于后续操作。

也可以使用 git log 看 stash 的提交结构:

$ git log --all --decorate --oneline --graph
*-.   46b69d2 (refs/stash) WIP on master: 3fa666d last commit message
|\ \
| | * 6def6bf untracked files on master: 3fa666d last commit message
| * 53095b4 index on master: 3fa666d last commit message
|/
[...]

3.5. 查看储藏内容

使用 git stash show 查看某个 stash 的修改:

$ git stash show 1
$ git stash show 0
 deletedfile | 0
 trackedfile | 1 +
 2 files changed, 1 insertion(+)

如果想查看未跟踪文件的修改,加上 --include-untracked

$ git stash show --include-untracked 1
 untrackedfile | 0
 1 file changed, 0 insertions(+), 0 deletions(-)

3.6. 恢复与删除储藏

主要有三种操作:

  • apply:将 stash 的修改应用到当前工作区
  • pop:先 apply,然后 drop 掉该 stash
  • drop:删除某个 stash

使用方法:

$ git stash apply stash@{1}
$ git stash pop stash@{0}
$ git stash drop stash@{1}

⚠️ 注意:pop 操作会删除 stash,但如果当前工作区有冲突,它不会删除 stash。

3.7. 高级用法

  • git stash create:创建一个 stash 但不加入 stash 栈,返回其 commit hash:
$ git stash create
087d502f375249eb1306660cea5822aa5e79d78d
  • git stash store:将一个 hash 加入 stash 栈:
$ git stash store -m "custom message" 087d502
  • git stash branch:从某个 stash 创建一个新分支并切换过去:
$ git stash branch new-branch stash@{1}

4. 总结

✅ Git 的暂存机制是日常开发中最常用的功能,用于有选择地提交修改。

✅ 当我们需要临时切换分支或保存未完成的修改时,git stash 是非常实用的工具。

✅ 要注意默认情况下 stash 不包含未跟踪文件,如需保存,需加 --include-untracked

✅ stash 是一个栈结构,支持查看、恢复、删除等操作,也可以用于创建新分支。

✅ 在脚本中使用 createstore 可以更灵活地管理 stash。

在实际开发中,掌握好 Git 的 staging 和 stash 操作,可以显著提升开发效率和代码管理能力。


原始标题:Guide to Git Staging and the Stash