TL;DR
To see file names:
git ls-tree -r stash^3
, to see contents:
git show stash^3
.
Long
如何展示存储的内容,包括未跟踪的文件?大部分情况下,未跟踪的文件通常不在存储中。但有一些例外,您正在使用其中一个例外的方法,稍后我将讲到它。但要展示这些文件比较麻烦。使用这些文件更加困难。我的意见是,
git stash
前端需要在这方面进行一些改进。首先,了解Git内部工作原理非常重要。Git仓库主要是一对数据库。一个(通常是最大的)保存提交和其他Git对象。较小的数据库保存名称:分支名称、标签名称、远程跟踪名称等。分支名称和标签名称等都是通用引用或参考的特定形式。引用保存单个Git哈希ID,最常见的是提交哈希ID。每个提交本身还可以保存一个或多个先前提交的哈希ID。这些是提交的父项。对于普通(非存储)提交,这些通常形成一个简单的链:最后一个提交作为其父项,记住倒数第二个提交。那个倒数第二个提交记住它的父项——最后提交的祖父,并且祖父记住另一个父项,依此类推。具有两个父项的提交是合并提交,这是其定义。因此,分支名称只是保存我们希望说是该分支上/中/的最后一个提交的哈希ID。多个名称可以选择相同的哈希ID,哈希ID可以通过从其尖端提交开始向后工作而从某个分支可达。因此,在Git中,提交通常位于多个分支上。提交本身包含所有已跟踪文件的快照。Git通过将跟踪的文件写入Git的索引,然后使用
git write-tree
将它们写入内部树对象,然后使用
git commit-tree
编写提交对象,以使用由git write-tree编写的树(s)。因此,所有提交都源自索引。任何索引中的文件都是跟踪的。所以这让我们有点困惑。
1分支名称和远程跟踪名称必须仅包含提交哈希 ID;标签名称更加灵活。
暂存区
特殊的引用refs/stash
(如果存在)指向一个提交。这就是stash@{0}
提交。(它在stash@{1}
及以上的reflog条目也指向每个提交。)因此,当存在时,暂存区由提交组成。这些提交不在任何分支上,而是通过refs/stash
找到。
正常的暂存区形式类似于合并提交,但并不完全相同。 git stash
所做的是创建两个提交,使用上面描述的非常低级别的git write-tree
和git commit-tree
方法。根据你在运行git stash save
或git stash push
时暂存区中的内容,第一个提交很容易:当你执行git write-tree
时,它已经将暂存区的内容写入,因此这两个命令放在一起,将创建这个暂存区提交,我称之为i
(git stash
文档称之为I
)。
第二个提交要棘手一些,但基本上,git stash
所做的是运行git add -u
(尽管没有实际使用git add -u
,从而在各种版本的git stash
中引入bug,其中一些在某些情况下会产生错误的工作树提交)。这将更新暂存区,以保存所有被跟踪文件到它们在工作树中的状态。然后,git write-tree
紧随其后,再加上git commit-tree
,可以为你的工作树创建一个良好的快照,当然不包括任何未被跟踪的文件。
因为git stash
正在使用这些底层命令,所以它可以使工作树提交(我称之为w
)具有其选择的任何父提交。它将使该提交同时拥有i
和HEAD
作为其两个父提交:
...--o--o <-- branch (HEAD)
|\
i-w
i
提交看起来像普通提交一样,
w
提交类似于合并。实际上它不是一个合并 - 它只是你的工作树的快照,作为添加到索引中的内容,但它的第一个父提交是您当前分支的尖端提交,第二个父提交是提交
i
。
进行此存储后,
git stash save
执行
git reset --hard
,使您的索引和工作树与
HEAD
匹配。
HEAD
本身从未移动,并且未跟踪的文件未被保存并且不受影响。
但是,当您执行
git stash save -u
或
git stash save -a
时,Git 会创建一个我称之为
u
的第三个提交。 这个第三个提交使用已清除所有已跟踪文件的索引,并使用您的一些或所有未跟踪文件加载,就好像在特定文件名上使用了
git add --force
一样。进入这个第三个提交的文件集取决于您是否使用了
-u
或
-a
:
-u
枚举未被忽略的未跟踪文件,而
-a
枚举所有未跟踪文件,即使它们被忽略。Git 将这些文件复制到(好吧,并且是)索引中,并创建此
u
提交,没有任何父提交,在最终的
w
提交之前。然后它使
w
提交,
u
作为它的第三个父提交。
...--o--o <-- branch (HEAD)
|\
i-w
/
u
因此,
w^1
是分支尖端的提交记录,
w^2
是索引提交记录
i
,
w^3
是提交记录
u
。这个
w
继续类似于合并提交记录——这次是多爪合并提交记录——但它的快照与任何双提交储藏记录相同。
进行了
u
提交记录后,
git stash save
或
git stash push
现在从您的工作树中
移除所有在
u
提交记录中的文件。如果某些在
u
提交记录中的文件也被忽略,则仍会被删除。
如果Git无法将
u
提交记录提取到您当前的工作树中,则应用三个提交储藏记录将失败。因此,知道第三个
u
提交记录中有什么肯定是有用的。但是没有
git stash ____
可以显示提交记录
u
是否存在,更不用说其中有什么了。因此,我们必须退回到较低级别的Git命令。
特别地,由于
u
是一个
根提交,因此
git show
将其与
空树进行差异比较。如果不想进行完全差异比较,则可以使用
git show --name-only
或
git ls-tree -r
获取文件列表。要为提交记录
u
命名,我们可以为任何储藏提交记录指定名称——指向
refs/stash
的任何
w
提交对象或其之一reflog条目——并添加
^3
后缀以表示
第三个父项。如果储藏只有
w
和
i
,则
^3
将失败:没有第三个父项,因此没有可显示的内容。
2如果愿意,实际上可以将它们放在一个分支上,但结果最多是丑陋的。
3在内部,
git stash
使用临时索引而不是真正的/主索引,以使事情变得更容易。它也用于
w
提交记录。虽然有一个特殊的索引
theindex,用于跟踪您的工作树,但您随时可以创建一个临时索引,将其路径名放入
GIT_INDEX_FILE
中,并使用该临时索引而不是使用区分索引的Git命令。对于需要创建提交记录(需要使用索引)但不想在此过程中干扰索引的任何命令,这非常方便。