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