顾乔芝士网

持续更新的前后端开发技术栈

Git 暂存详解(git提交暂存文件)

git stash 命令可以将您在工作目录中的修改临时暂存(stash)起来,以便您能切换上下文处理其他任务,后续再重新应用这些修改。当您处于代码修改中途需要快速切换分支或处理紧急问题,但又不希望提交不完整的代码时,这个功能非常实用。


暂存你的工作


执行 git stash 会将所有未提交的修改(包括已暂存和未暂存的变更)临时保存,并清空当前工作区。例如:

$ git status
On branch main
Changes to be committed:
new file:   style.cssChanges not staged for commit:
modified:   index.html
$ git stash
Saved working directory and index state WIP on main: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage
$ git status
On branch main
nothing to commit, working tree clean

此时您可以自由地进行其他修改、提交、切换分支等操作,后续随时可以重新应用暂存内容。

注意:暂存内容仅存储在本地仓库,推送时不会上传到服务器。

重新应用暂存修改


使用 git stash pop 可重新应用最近一次暂存修改并删除暂存记录:

$ git status
On branch main
nothing to commit, working tree clean
$ git stash pop
On branch main
Changes to be committed:
    new file:   style.css
Changes not staged for commit:
    modified:   index.html
Dropped refs/stash@{0} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)

若想保留暂存记录,可使用 git stash apply:

$ git stash apply
On branch main
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

这在需要将同一份暂存应用到多个分支时非常有用。

暂存未跟踪或忽略的文件


默认情况下,git stash 不会暂存:

  • 未添加到暂存区的新文件(untracked files)
  • 被 .gitignore 忽略的文件

例如当存在未跟踪文件时:

$ script.js

$ git status
On branch main
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Untracked files:

    script.js

$ git stash
Saved working directory and index state WIP on main: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage

$ git status
On branch main
Untracked files:

    script.js

使用 -u 选项包含未跟踪文件:

$ git status
On branch main
Changes to be committed:
new file:   style.cssChanges not staged for commit:
modified:   index.htmlUntracked files:
    script.js
$ git stash -u
Saved working directory and index state WIP on main: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage
$ git status
On branch main
nothing to commit, working tree clean

使用 -a 选项可额外包含被忽略的文件:

管理多个暂存记录


你可以多次运行 git stash 来创建多个暂存,然后使用 git stash list 查看它们。默认情况下,暂存会被标识为一个“WIP”(工作进行中),它基于你创建暂存时所在的分支和提交。但随着时间的推移,你可能会难以记住每个暂存具体包含了什么内容:

$ git stash list
stash@{0}: WIP on main: 5002d47 our new homepage
stash@{1}: WIP on main: 5002d47 our new homepage
stash@{2}: WIP on main: 5002d47 our new homepage

为了提供更多的上下文信息,一个好的做法是为你的暂存添加描述,使用 git stash save "描述信息":

$ git stash save "add style to our site"
Saved working directory and index state On main: add style to our site
HEAD is now at 5002d47 our new homepage

$ git stash list
stash@{0}: On main: add style to our site
stash@{1}: WIP on main: 5002d47 our new homepage
stash@{2}: WIP on main: 5002d47 our new homepage

默认情况下,git stash pop 会重新应用最近创建的暂存:stash@{0}。 你也可以通过传入暂存的标识符作为最后一个参数,来选择要重新应用的暂存,例如:

$ git stash pop stash@{2}

查看暂存差异


你可以使用 git stash show 来查看某个暂存的摘要信息:

$ git stash show
 index.html | 1 +
 style.css | 3 +++
 2 files changed, 4 insertions(+)

或者使用 -p 选项(或 --patch),来查看某个暂存的完整差异(diff):

$ git stash show -p
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..d92368b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,3 @@
+* {
text-decoration: blink;
+}
diff --git a/index.html b/index.html
index 9daeafb..ebdcbd2 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
+<link rel="stylesheet" href="style.css"/>

部分暂存


你也可以选择仅暂存某个文件、一组文件,或者某些文件中的个别更改。如果你在运行 git stash 时加上 -p(或 --patch)选项,它会遍历工作目录中每个更改的“块(hunk)”,并询问你是否希望暂存它:

$ git stash -p
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..d92368b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,3 @@
+* {
text-decoration: blink;
+}
Stash this hunk [y,n,q,a,d,/,e,?]? y
diff --git a/index.html b/index.html
index 9daeafb..ebdcbd2 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
+<link rel="stylesheet" href="style.css"/>
Stash this hunk [y,n,q,a,d,/,e,?]? n

你可以按 ? 键查看全部块操作命令。常用的有:

命令

描述

/

通过正则表达式搜索补丁(hunk)

?

显示帮助信息

n

不暂存当前补丁(hunk)

q

退出(任何已经选择的补丁会被暂存)

s

将当前补丁拆分成更小的补丁

y

暂存当前补丁

虽然没有明确的“取消”命令,但按下 CTRL+C(发送 SIGINT)可以中止暂存过程。

从暂存中创建分支


如果你当前分支的更改与你的暂存内容发生了分歧,那么在使用 git stash pop 或 git stash apply 时可能会产生冲突。为避免这种情况,可以使用 git stash branch 命令,从暂存中创建一个新分支:

$ git stash branch add-stylesheet stash@{1}
Switched to a new branch 'add-stylesheet'On branch add-stylesheet
Changes to be committed:
new file:   style.cssChanges not staged for commit:
modified:   index.htmlDropped refs/stash@{1} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)

此命令会基于创建暂存时的提交检出一个新分支,并将暂存的更改应用到该分支上。

清理暂存


如果你决定不再需要某个暂存项,可以使用 git stash drop 删除它:

$ git stash drop stash@{1}
Dropped stash@{1} (17e2697fd8251df6163117cb3d58c1f62a5e7cdb)

你也可以删除所有暂存项:

$ git stash clear

Git Stash 的工作原理


如果你只是想知道如何使用 git stash,那么上面的内容已经足够。但如果你对 Git(尤其是 git stash)的底层实现感兴趣,请继续阅读。

实际上,stash 是以提交对象(commit objects)的形式存储在你的仓库中的。.git/refs/stash 是一个特殊引用,它指向最近一次创建的 stash,而之前创建的 stash 项则可以通过该引用的 reflog 访问。这就是你用 stash@{n} 来引用暂存的原因:你实际上是在引用 stash 引用的第 n 个 reflog 条目。

由于 stash 本质上是一个提交,你可以使用 git log 来查看它。

根据你暂存的内容,一次 git stash 操作会创建两个或三个新的提交。如下图所示:

  • stash@{0}:一个新提交,用于保存你工作目录中受 Git 跟踪的文件的内容。
  • stash@{0} 的第一个父提交:你运行 git stash 时 HEAD 指向的原始提交。
  • stash@{0} 的第二个父提交:代表你运行 git stash 时的暂存区(index)。
  • stash@{0} 的第三个父提交(如果有):表示运行 git stash 时工作目录中未跟踪文件的提交。仅在以下情况下创建:
  • 工作目录中确实有未跟踪的文件;
  • 并且你在执行 git stash 时使用了 --include-untracked 或 --all 选项。

Git stash 如何将你的工作目录和暂存区编码为提交:

  • 在暂存前,你的工作目录可能包含对已跟踪文件的更改、未跟踪文件和被忽略的文件。这些更改中可能还有部分已被暂存。
  • 执行 git stash 时,会将对已跟踪文件的更改编码为两个新的提交:一个表示未暂存的更改,一个表示已暂存的更改。同时,refs/stash 会更新指向这些提交。
  • 使用 --include-untracked 选项时,未跟踪文件的更改也会被编码为一个额外的提交。
  • 使用 --all 选项时,未跟踪文件和被忽略文件的更改会一起被编码到同一个提交中。

当你运行 git stash pop 时,上述提交中的更改会被应用到你的工作目录和暂存区中,同时 stash reflog 会更新以移除被弹出的提交。注意,这些被弹出的提交不会立即删除,但会成为将来垃圾回收的候选对象。

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言