Git stash 暂存区详解
git stash暂时搁置(或隐藏)我们对工作副本所做的更改,以便我们可以处理其他事情,处理完其他事情之后再回来并重新应用它们。
如果我们需要快速切换上下文并处理其他事情,当时我们当前代码正改了一部分,还没有准备好对其进行提交。那么这种情况下 git stash 就很有用了。
搁置当前的修改
git stash命令获取我们未提交的更改(暂存和未暂存),将它们保存以备后用,然后从工作副本中恢复它们。
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: style.css
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: index.html
$ git stash
Saved working directory and index state WIP on master: 4b56b56 init
$ git status
On branch master
nothing to commit, working tree clean
此时,我们可以自由地进行更改、创建新提交、切换分支以及执行任何其他 Git 操作;然后在准备好后返回并重新应用先前的搁置的修改处。
注意
,stash 位于本地 Git 仓库的;推送时,stash隐藏的修改不会传输到服务器。
重新回到被隐藏的更改处
我们可以使用git stash pop
命令重新应用以前隐藏的更改
$ git stash pop
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: style.css
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: index.html
Dropped refs/stash@{0} (f6f919bf50879b333b7fdc8ea3cb65ced20fce58)
实际执行结果如下图所示
或者,我们可以使用 git stash apply
将之前隐藏的更改重新应用到工作副本,但是和 git stash pop
不同, 并不会将他们从stash中删除。
$ git stash apply
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: style.css
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: index.html
如果想将相同的隐藏更改应用于多个分支,上面的命令将非常有用。
警告
:默认情况下,Git不会存储对未跟踪或忽略的文件所做的更改。
隐藏未跟踪或忽略的文件
默认情况下,运行git stash将隐藏:
- 已添加到索引中的更改(分阶段更改)
- 对 Git 当前跟踪的文件所做的更改(未暂存的更改)
但它不会存储:
- 工作副本中尚未跟踪的新文件
- 被忽略的文件
因此,如果我们在上面的示例中添加第三个文件 scripy.js
,但不暂存(即我们不运行git add),git stash则不会存储它。
$ 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
选项 (或--include-untracked
) 则可以告诉git stash 隐藏您未跟踪的文件:
$ 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 -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
选项(或 --allgit)来隐藏对忽略文件的更改。
管理多个存储
我们使用 git stash
不限于一个单一的隐藏处。我们可以使用git stash
多次运行以创建多个 stash,然后用于git stash list
查看它们。
$ 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 "message"
$ 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}
我们还可以通过指定标识符选择要重新应用的 stash,例如:
$ git stash pop stash@{2}
查看存储差异
我们可以使用命令git stash show
查看存储摘要
$ git stash show
index.html | 1 +
style.css | 3 +++
2 files changed, 4 insertions(+)
或者通过-p
选项(或--patch
)查看存储的完整差异:
$ 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
),它将遍历工作副本中每个更改的“大块”并询问我们是否希望将其隐藏:
$ 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
我们可以输入 ?
来查看完整的命令列表。常用的有:
命令 | 描述 |
---|---|
/ | 通过正则表达式搜索块 |
? | 帮助 |
n | 指定不要隐藏的块 |
q | 退出 |
s | 把大块分成更小的块 |
y | 把块隐藏起来 |
没有明确的“中止”命令,但点击CTRL-C(SIGINT) 将中止存储过程。
清除所有的隐藏
如果决定不再需要某个特定的存储库,可以使用 git stash drop
命令将其删除
$ git stash drop stash@{1}
Dropped stash@{1} (17e2697fd8251df6163117cb3d58c1f62a5e7cdb)
或者,我们可以使用以下命令删除所有存储:
$ git stash clear
git stash 的工作原理
如果只是想知道如何使用git stash,可以在此处停止阅读。但是,如果你对 Git(和git stash)的工作原理感到好奇,请继续阅读!
存储实际上在仓库中编码为提交对象。位于.git/refs/stash
的特殊引用指向最近创建的隐藏,先前创建的隐藏由隐藏引用的reflog引用。这就是为什么我们通过stash@{n}
引用stashes:实际上是指stash ref的第n个reflog条目。
由于 stash 只是一个提交,可以使用以下命令检查它git log:
$ git log --oneline --graph stash@{0}
* 8876003 (refs/stash) WIP on master: 4b56b56 init
|\
| * b9999ee index on master: 4b56b56 init
|/
* 4b56b56 (HEAD -> master) init
* ed18a3a 修改hello.txt 文件
* 33342da 输入本次提交的信息,用来备注本次修改的内容lll
实际运行如下图所示
git stash如何将工作树和索引编码为提交对象:
在存储之前,工作树可能包含对跟踪文件、未跟踪文件和忽略文件的更改。其中一些更改也可能会在索引中暂存。
调用git stash
将对跟踪文件的任何更改编码为 DAG 中的两个新提交:一个用于未暂存的更改,另一个用于索引中暂存的更改。refs/stash
更新特殊引用以指向它们。
使用--include-untracked
选项还将对未跟踪文件的任何更改编码为附加提交。
使用--all
选项包括对任何被忽略文件的更改以及对同一提交中未跟踪文件的更改。
当运行git stash pop
命令时,上述提交的更改用于更新工作副本和索引。
注意
,弹出的提交不会立即删除,但会成为未来垃圾收集的候选者。