我写了一篇博客文章Git Stash Internals与我对这个主题的理解。
我在下面分享它的要点,希望这可以帮助你和其他人。欢迎反馈。
git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: CONTRIBUTING.md
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: README.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
LICENSE
有3个部分:
Changes to be committed
表示
索引的内容。( CONTRIBUTING.md
)
Changes not staged for commit
表示在工作目录中
修改的跟踪文件。( )README.md
Untracked files
表示 git 还不知道的文件 ( LICENSE
)
在这个例子中,我假设只创建了一个存储并且我stash@{0}
用来引用它。
下表描述了根据使用的 git stash 命令隐藏了哪些文件以及在哪里(提交)。
Git 存储命令 |
修改后的跟踪文件(工作目录) |
指数 |
未跟踪的文件 |
忽略 |
|
README.md |
CONTRIBUTING.md |
LICENSE |
temp/stash.out |
git stash |
☑️ stash@{0} |
☑️ stash@{0}^2 |
⤬ </td>
| ⤬</td>
|
git stash -u |
☑️ stash@{0} |
☑️ stash@{0}^2 |
☑️ stash@{0}^3 |
⤬</td>
|
git stash -a |
☑️ stash@{0} |
☑️ stash@{0}^2 |
☑️ stash@{0}^3 |
☑️ stash@{0}^3 |
现在,让我们看一下每个git stash
命令的作用。
git stash
默认情况下,git stash
预留:
- 任何被修改且未被忽略的跟踪文件:
README.md
- 指数:
CONTRIBUTING.md
它不会分别保留未跟踪或忽略的文件,例如LICENSE
和temp/stash.out
.
git status
On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
LICENSE
nothing added to commit but untracked files present (use "git add" to track)
相反,如果我想存储已跟踪和未跟踪的文件,这就是我当前正在进行的所有工作(README.md
和LICENSE
)加上索引的内容(CONTRIBUTING.md
),那么我需要使用git stash -u
,如下所示:
git stash -u
git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean
要同时存储被忽略的文件,您可以git stash -a
改用。
存储堆栈
添加 stash 时,git 创建一个stash commit,将其推送到 stash 堆栈的顶部。这会将现有的存储条目向下移动(如果有)。引用stash@{0}
始终表示存储堆栈的顶部。每次您存放其他东西时,它都会被向下推,因此:
stash@{0}
表示最近创建的存储,
stash@{1}
表示创建的倒数第二个存储,
stash@{2}
表示创建的倒数第三个存储,依此类推。
现在我们知道了 stashed 是什么,让我们看一下它在内部的存储方式。
现在我们已经设置好了场景,让我们来看看事情是如何在幕后工作的。
藏匿处有什么?
让我们弄清楚我们最近的存储库中有什么。
本节假设我们git stash -u
在示例的存储库中运行,所以我们最终得到了这个日志。
现在让我们看一下 stash@{0} 提交。
git log --format=raw -1 stash@{0}
commit 49482afa4ab999deada67c65dc5d38be89aed867
tree 936c8b08ac5a8e91bb6cc38387d2cca93167e0ae
parent 031ca106c13b1603675ea1ce8da8b3da852e27cd
parent b558b9e7621fe508c7c18713cd62c78e80e2017e
parent dfac0d769262fa4b8ea40003d24052c4509a7f3a
author Eric Bouchut <ebouchut@gmail.com> 1627056522 +0200
committer Eric Bouchut <ebouchut@gmail.com> 1627056522 +0200
WIP on master: 031ca10 Add README
请注意,存储提交的父级按顺序列出(第一个 ( 031ca10
),然后是第二个 ( b558b9e
),然后是第三个 ( dfac0d7
))。
在这种情况下,隐藏提交stash@{0}
( 49482a
) 是与 3 个父级的合并提交,因为我们隐藏了未跟踪的文件(默认为 2 个父级)。
它还包含在存储时修改的工作目录的非忽略文件。
让我们见见家长:
stash@{0}^1
( 031ca10
) 表示存储提交的第一个父级。
这是存储时的当前提交 ( HEAD
)。
stash@{0}^2
( b558b9e
) 表示存储提交的第二个父级。
它包含存储时索引中存在的变更集。索引又名
。作为集结地。这是您添加的文件在提交之前存储的位置。git add
stash@{0}^3
( dfac0d7
) 表示存储提交的第三个父级。
它包含存储时工作树中存在的未跟踪文件( -u
) 和忽略文件( )。仅当您使用任何or选项时才创建它。-a
git stash
-u
-a
为什么我们需要深入研究它的内部运作git stash
?
直到 2.32 版本,git 没有提供一种简单的方法来列出和显示隐藏提交中未跟踪的文件。这就是为什么我们需要了解git stash
内部结构才能做到这一点。您现在已准备好了解接下来会发生什么。
存储提交的工作目录中的修改文件
以下是如何在最近的存储提交的工作目录中列出修改的文件:
git log -m --first-parent -1 --format='' --name-only 'stash@{0}'
在这里,我们深入研究合并提交 ( -m
) 并仅关注第-1
一个父 ( ) 的第一个提交 ( --first-parent
),即存储提交本身。
ℹ️ 默认情况下,git log 不显示有关合并提交的任何父级的详细信息,除非我们使用-m
并且当我们使用此选项时,它会显示每个父级的请求。由于这不是我们想要的,我们只限制第一个父母。
无论出于何种原因,即使使用--name-only
,git log
除了文件名外,也会显示非请求信息(提交 SHA1、日期和作者)。我在 git 版本 2.32.0 中注意到了这个问题。这就是为什么我--format=''
用作解决方法来删除它们。
现在,这里是如何查看 最近一次存储提交的工作目录的修改文件中的更改:
git log -m --first-parent -1 -p 'stash@{0}'
# Stashed Files of a Stash Commit
The command below **lists** the **staged files** of the most recent stash commit.
```lang-shell
git log --name-only -1 --format='' 'stash@{0}^2'
为了
在此存储提交中获取(更改集中的)索引的内容:
git log -1 -p 'stash@{0}^2'
隐藏提交的未跟踪文件
以下是如何在最近的存储提交中列出未跟踪的文件。
从git 版本 2.32
开始git show
,现在可以--only-untracked
选择列出存储的未跟踪文件。
ℹ️ 如果您曾经git stash -a
也存储被忽略的文件,这也会列出被忽略的文件。
git stash show --only-untracked --name-only 'stash@{0}'
在 git 版本 2.32 之前,我们应该使用以下两种替代方法之一:
git show --name-only 'stash@{0}^3:'
请注意末尾的冒号 ( :
)。
git ls-tree -r 'stash@{0}^3' --name-only
以下是如何在最近的存储提交中查看未跟踪文件(以及忽略的文件(如果有))的内容。
从git 版本 2.32开始,您可以 --only-untracked
使用git show
.
git stash show --only-untracked -p 'stash@{0}'
在 git 版本 2.32 之前,请改用:
git log -p 'stash@{0}^3'