'git stash show -p stash@{N}' 和 'git show stash@{N}' 之间的区别



我认为它们应该基本相同,但是当我尝试时

$ git stash show -p stash@{N}

$ git show stash@{N}

后者显示了一些额外的提交信息,但实际的差异要短得多。(前者显示大约十几个文件,但后者只显示一个。

那么,两者之间究竟有什么区别,为什么它们不同?

我也可以依靠git diff stash@{M} stash@{N}之类的东西来正确吗?

储物袋

git stash保存的东西就是我所说的"藏匿袋"。 它由两个单独的1提交组成:"索引"提交(暂存区域)和"工作树"提交。 工作树提交是一种有趣的合并提交。

让我在这里再次绘制它(请参阅参考答案以获取更长的版本),只是为了正确说明它。 为简单起见,我们假设您有一个小存储库,上面只有一个分支和三个提交,AC。 您在一个分支上并进行一些更改,然后运行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

这里的重点是"储藏袋",一对index 和work-tree 提交,仍然像以前一样挂在同一个提交上。 提交无法更改,存储包提交也是如此。

但是现在您通过进行一些更改(仍在master)并再次运行git stash save来创建新的存储。

旧的储物袋会怎样? "参考名称">2stash,现在指向新的储物袋。 但是旧的藏袋提交仍然存在。 他们现在需要一个"reflog"样式的名称,stash@{1}.3

无论如何,你现在拥有的是这个:

A - B - C - D - E     <-- HEAD=master
|      |
i-w     i-w   <-- stash
.
-------------- stash@{1}

(当你使用git stash drop时,存储脚本只是操作stashref 的 reflog 以删除丢弃的存储袋的 ID。 这就是为什么所有"更高"的都被重新编号的原因。 实际的储物袋本身在下一个git gc被垃圾收集。

接下来的一点是理解正在发生的事情的关键。

每当 git 需要你命名一个特定的提交时,你可以通过许多不同的方式中的任何一种来做到这一点。

每个提交都有一个"真实名称",即您看到的大丑陋 SHA-1 哈希,值如676699a0e0cdfd97521f3524c763222f1c30a094。 你可以写那个。 它总是意味着相同的提交。 提交永远无法更改,这是提交全部内容的加密哈希,因此如果该特定提交存在,则该值始终是其名称。

不过,这对人们来说不是一个好名字。 所以我们有别名:像分支和标签名称,相对名称,如HEADHEAD~2,以及 reflog 样式的名称,如HEAD@{yesterday}master@{1}。 (git rev-parse,有一个命令可以将这样的名称字符串转换为哈希值。 尝试一下:运行git rev-parse HEADgit rev-parse stash等。 git 中的大多数东西都使用git rev-parse或它的老大哥,它做更多的事情,git rev-list,将名称转换为 SHA-1 值。

(有关如何命名修订的完整说明,请参阅 gitrevisions。 Git 使用 SHA-1 不仅仅是提交,但在这里让我们只考虑提交。

Git stash show、git show 和 git diff

好的,最后,我们可以讨论您的git showgit stash show,以及git diff等等。 让我们先解决git stash show,因为这是您应该与藏匿一起使用的那个。 此外,git stash子命令将验证您命名的提交(或者,如果您没有命名提交,则通过stash引用找到的提交)是否"看起来像"一个存储,即是这些有趣的合并提交之一。

如果你运行git stash show -p,git 会给你显示一个差异(-patch)。 但它究竟在显示什么?

回到带有储物袋的图表。 每个储藏袋都挂在一个特定的提交上。 上面,"主"存储现在挂在提交E上,而stash@{1}更早的存储现在挂在C上。

git stash show -p所做的是将该存储的工作树提交(w)与挂起该存储的提交进行比较。

你当然可以自己做。 假设你想比较stash中挂起提交Ew与可以通过分支名称命名的Emaster。 所以你可以运行:git diff master stash. 这里的名字stash指的是(当前)存储提交w,而master指的是提交E,所以这会产生与git stash show -p stash完全相同的补丁。 (而且,如果您想将stash@{1}中的w与提交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是提交Ci的正常合并,产生w。 毕竟这不是一个正常的合并,尽管组合的差异实际上可能很有用,只要你知道你在看什么。 阅读git diff-tree下的--cc文档,详细了解其工作原理,但我会注意到--cc暗示-c其中包括以下位:

。仅列出从所有父级修改的文件。

stash的情况下,如果您在运行git stash之前git add了 -ed 文件,因此i-vs-wdiff 为空,您将不会在此处的输出中看到这些文件。

最后,如果你git diff stash@{M} stash@{N}:这只是要求git diff比较不同的work-tree提交。 这有多大意义取决于你比较的内容,这通常取决于储物袋的贴在哪里。


1两三个,真的,但我要把它画成两个。 你会得到两个带有git stash save的提交(或普通git stash,这意味着git stash save)。 如果添加-u-a选项以保存未跟踪的文件或所有文件,则会获得三次提交。 这会影响存储还原,但不会影响git stash show命令的输出。

2"引用名称"只是一个名称,更像是分支或标签名称。 引用名称有许多可能的形式。 分支和标签只是具有特殊用途的名称。 "远程分支"是这些引用的另一种形式,"stash"也是引用。

事实上,HEAD只是另一个参考,尽管它是一个非常特殊的参考。我是如此重要,以至于如果您删除HEAD文件,git 将决定您的存储库毕竟不再是存储库。

除了一些特殊情况(HEADORIG_HEADMERGE_HEAD等)之外,引用都以字符串refs/开头。 分支以refs/heads/开头,标签以refs/tags/开头,"远程分支"以refs/remotes/开头。 换句话说,引用有一个"名称空间",通常以refs/开头,然后在下面加上另一个单词来标识它们所在的位置。

stash 引用拼写为refs/stash(并停在那里,没有refs/stash/jimmy_kimmel或类似的东西)。

3事实上,这确实使用了 reflog。 这意味着,除其他事项外,"主要"存储refs/stash意志可能会过期。 (幸运的是,正如 musiphil 所指出的,自 git 1.6.0 以来的默认设置是这些不会过期;您必须为它们配置过期时间才能实现这一点——这可能不是您想要的。

4它这样做的巧妙方法,使用后缀^符号,在我的另一个答案中详细说明。

5如果你想看看这些藏袋中的index提交怎么办? 啊,好问题!:-) 存储脚本没有一个好的答案。 查看这些内容的简单方法是使用^2后缀来命名每个存储的第二个父项,即提交i。 而且,如果你有一个包含未跟踪或所有文件的第三个提交的存储,那就是第三个父项:提交w看起来像一个三父级合并,stash^3得到第三个。 但同样,w不是正常的合并,所以很棘手。 查看存储的所有部分的最好简单方法可能是使用git stash branch将其转换为自己的单独分支。

我相信这是由于git单独存储工作目录和索引的怪癖。git stash show -p stash@{N}将显示存储中的所有更改,包括添加到舞台的更改。 但是git show stash@{N}不包括在存储之前阶段的更改。 似乎git stash命令足够聪明,可以将它们组合在一起,而git show只是向您显示 blobstash@{0}

的内容是的,git diff stash@{M} stash@{N}将按您的预期工作。

相关内容

  • 没有找到相关文章

最新更新