我认为它们应该基本相同,但是当我尝试时
$ git stash show -p stash@{N}
和
$ git show stash@{N}
后者显示了一些额外的提交信息,但实际的差异要短得多。(前者显示大约十几个文件,但后者只有一个。)
那么,两者之间究竟有什么区别,为什么它们不同呢?
我也可以依靠git diff stash@{M} stash@{N}
正确的东西吗?
保存的东西git stash
就是我习惯称之为“藏匿袋”的东西。它由两个1单独的提交组成:“索引”提交(暂存区)和“工作树”提交。工作树提交是一种有趣的合并提交。
让我在这里再画一次(请参阅参考答案以获得更长的版本),只是为了正确说明它。为简单起见,我们假设您有一个小型 repo,其中只有一个分支和三个提交,A
通过C
. 您在一个分支上并进行一些更改,然后运行git stash save
(或只是简单的git stash
)。这就是你得到的:
A - B - C <-- HEAD=master
|\
i-w <-- the "stash"
现在您可能会创建(或切换到)另一个分支,但为了说明起见,假设您将存储区留在那里并在以下位置进行更多“常规”提交master
:
A - B - C - D - E <-- HEAD=master
|\
i-w <-- stash
这里的重点是“stash-bag”,即i
ndex 和w
ork-tree 提交对,仍然像以前一样挂在同一个提交上。提交不能更改,stash-bag 提交也是如此。
但是现在您通过进行一些更改(仍在 on 时)并再次运行来创建一个新的存储。master
git stash save
旧的储物袋怎么办?“参考名称” 2 stash
现在指向新的存储袋。但是旧的 stash-bag 提交仍然存在。他们现在需要一个“reflog”样式名称,stash@{1}
. 3
无论如何,你现在拥有的是这样的:
A - B - C - D - E <-- HEAD=master
|\ |\
i-w i-w <-- stash
.
-------------- stash@{1}
(当您使用git stash drop
时,stash 脚本只是为 ref 操作 reflogstash
以删除丢弃的 stash-bag 的 ID。这就是所有“更高”的被重新编号的原因。实际的 stash-bag 本身是在下一个git gc
. )
接下来的一点是了解正在发生的事情的关键。
每个提交都有一个“真实名称”,即您看到的丑陋的 SHA-1 哈希值,例如676699a0e0cdfd97521f3524c763222f1c30a094
. 你可以这样写。它总是意味着相同的提交。提交永远无法更改,这是提交的全部内容的加密哈希,因此如果该特定提交完全存在,则该值始终是它的名称。
不过,这对人们来说不是一个好名字。所以我们有别名:诸如分支和标签名称之类的东西,以及诸如HEAD
and之类的相对名称,以及诸如orHEAD~2
之类的引用日志样式名称。(有一个命令 ,可以将这样的名称字符串转换为哈希值。试试看:运行,等等。git 中的大多数东西都使用或者它的老大哥做更多的事情, 将名称转换为 SHA- 1 个值。)HEAD@{yesterday}
master@{1}
git rev-parse
git rev-parse HEAD
git rev-parse stash
git rev-parse
git rev-list
(有关如何命名修订的完整描述,请参阅gitrevisions 。Git使用 SHA-1 的不仅仅是提交,但这里让我们只考虑提交。)
好的,最后,我们可以得到你的git show
vs git stash show
,git diff
等等。让我们git stash show
先解决这个问题,因为那是您应该与 stash 一起使用的那个。此外,git stash
子命令将验证您命名的提交(或者,如果您未命名提交,则通过stash
引用找到的提交)“看起来像”一个存储,即,是这些有趣的合并提交之一。
如果你运行git stash show -p
,git 会显示一个差异(-p
atch)。但它究竟显示了什么?
回到带有储物袋的图表。每个 stash-bag 都挂在一个特定的提交上。上面,“主要”存储现在挂在 commitE
上,而stash@{1}
之前的 stash 挂在C
.
什么git stash show -p
是将存储的工作树提交,w
,与存储挂起的提交进行比较。4
你当然可以自己做。假设你想比较w
in stash
,它挂在 commitE
上,与 commit E
,它可以由 branch-name 命名master
。所以你可以运行:git diff master stash
. 这里的名称stash
指的是(当前) stash commit w
,并且master
指的是 commit E
,所以这会产生与git stash show -p stash
. (而且,如果你想比较w
instash@{1}
和 commit C
,你只需要运行git diff
这样你就命名这两个提交。当然它更容易git stash show -p stash@{1}
。)5
平原git show
呢?这有点复杂。 git show
很高兴显示提交,并且您给了它一个stash
参考(它stash
本身,或者一个 reflog 变体)。这是一个有效的提交标识符,它解析为一个w
存储袋中的一个工作树提交。但是git show
当它看到合并提交时会采取不同的行动。正如文档所说:
它还以由
git diff-tree --cc
.
所以git show stash@{1}
向您展示了一个“组合差异”,假设提交w
是提交C
和的正常合并i
,产生w
. 毕竟这不是一个正常的合并,虽然组合的差异实际上可能很有用,只要你知道你在看什么。阅读下面的--cc
文档git diff-tree
以详细了解它是如何工作的,但我会注意到这--cc
意味着-c
其中包括这一位:
... 仅列出所有父母修改过的文件。
在 a 的情况下stash
,如果您git add
在运行之前 -ed 文件git stash
,因此i
-vs- w
diff 为空,您将不会在此处的输出中看到这些文件。
最后,如果您git diff stash@{M} stash@{N}
:这只是要求git diff
比较不同的w
ork-tree 提交。这有多少意义取决于您要比较的内容,这通常取决于存储袋的连接位置。
1两个或三个,真的,但我要把它画成两个。你得到两个提交git stash save
(或一个普通的git stash
,这意味着git stash save
)。如果添加-u
or-a
选项来保存未跟踪文件或所有文件,您将获得三个提交。这会影响存储恢复,但不会影响git stash show
命令的输出。
2 “参考名称”只是一个名称,更像是一个分支或标签名称。有许多可能的参考名称形式。分支和标签只是具有特殊用途的名称。“远程分支”是这些引用的另一种形式,“stash”也是一个引用。
事实上,HEAD
只是另一个参考,虽然它是一个非常特殊的参考。我是如此重要,以至于如果您删除该HEAD
文件,git 将决定您的存储库毕竟不再是存储库。
除了一些特殊情况的例外——<code>HEAD、、、ORIG_HEAD
等等MERGE_HEAD
——引用都以字符串 开头refs/
。分支以 开头refs/heads/
,标签以 开头refs/tags/
,“远程分支”以 开头refs/remotes/
。换句话说,引用有一个“名称空间”,通常以另一个词开头,refs/
然后在其下方添加另一个词来标识它们的居住地。
存储参考是拼写的refs/stash
(并停在那里,没有refs/stash/jimmy_kimmel
或类似的东西)。
3事实上,这确实使用了 reflog。这意味着,除其他外,“主要”存储之外的存储refs/stash
可能会过期。(幸运的是,正如musiphil 所说,自 git 1.6.0 以来的默认设置是这些不会过期;您必须为它们配置过期时间才能实现这一点——这可能不是您想要的。)
4它使用后缀符号的巧妙方法在我的另一个答案中进行了说明。^
5如果您想查看i
这些存储包中的 ndex-commits 怎么办?啊,好问题!:-) stash 脚本没有一个好的答案。查看这些的简单方法是使用^2
后缀来命名每个存储的第二个父级,即i
提交。而且,如果您有一个包含未跟踪文件或所有文件的第三个提交的存储,那就是第三个父级:提交w
看起来像一个三父级合并并stash^3
获得第三个。但同样,w
这不是一个正常的合并,所以它很棘手。查看存储的所有部分的最佳简单方法可能是将其转换为自己的单独分支,使用git stash branch
.
我相信这是由于 git 分别存放工作目录和索引的怪癖。
git stash show -p stash@{N}
将显示存储中的所有更改,包括添加到舞台的更改。但是git show stash@{N}
,不包括在存储之前已上演的更改。该git stash
命令似乎足够聪明,可以将它们组合在一起,而git show
只是向您显示 blob 的内容stash@{0}
是的,git diff stash@{M} stash@{N}
将按您的预期工作。