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

标签 git git-stash git-show

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

$ git stash show -p stash@{N}


$ git show stash@{N}

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

那么,这两者究竟有什么区别,为什么会不同呢?

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

最佳答案

收纳袋

保存的东西 git stash是我调用 "stash bag" 的原因.它由两个单独的提交组成:“索引”提交(暂存区)和“工作树”提交。工作树提交是一种有趣的 merge 提交。

让我在这里再次绘制(请参阅更长版本的引用答案),只是为了正确说明它。为简单起见,让我们假设您有一个只有一个分支和三个提交的小型存储库,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 袋”,一对i索引和 w ork-tree 提交,仍然像以前一样挂起相同的提交。提交无法更改,对于 stash-bag 提交也是如此。

但是现在您通过进行一些更改(仍然在 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 脚本简单地操作 stash ref 的引用日志以删除丢弃的 stash-bag 的 ID。这就是为什么所有“更高”的都被重新编号。实际的 stash-bag 本身是在下一个 git gc 上收集垃圾。)

下一点是了解正在发生的事情的关键。

任何时候 git 需要你命名一个特定的提交,你都可以用许多不同的方式来完成。

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

不过,这对人们来说并不是一个好名字。所以我们有别名:像分支和标签名称这样的东西,以及像 HEAD 这样的相对名称。和 HEAD~2 , 以及类似 HEAD@{yesterday} 的 reflog 样式的名称或 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 show对比 git stash show , 和 git diff等等。让我们解决 git stash show首先,因为那是您应该与 stash 一起使用的那个。此外,git stash子命令将验证您命名的提交——或者,如果您未命名提交,则验证通过 stash 找到的提交。引用——“看起来像”一个 stash,即是这些有趣的 merge 提交之一。

如果您运行 git stash show -p , git 向您显示一个差异 ( -p atch)。但它究竟显示了什么?

回到带有储物袋的图表。每个 stash-bag 都卡在一个特定的提交上。上面,“主要”存储现在卡在提交 E 上。 ,以及 stash@{1}较早的藏品卡在 C .

什么 git stash show -p确实是比较存储的工作树提交,w , 针对存储挂起的提交。 4

你当然可以自己做。假设您要比较 wstash , 挂起提交 E , 反对提交 E ,可以通过分支名称master来命名.所以你可以运行:git diff master stash .这里的名字stash指的是(当前)存储提交 w , 和 master指的是提交 E ,所以这会产生与 git stash show -p stash 完全相同的补丁. (而且,如果您想将 w 中的 stash@{1} 与提交 C 进行比较,您只需要运行 git diff 这样您就可以命名这两个提交。当然,仅使用 git stash show -p stash@{1} 会更容易。)5

普通的怎么样git show ?这有点复杂。 git show很高兴展示一个提交,你给了它一个 stash引用(stash 本身,或 reflog 变体之一)。这是一个有效的提交标识符,它解析为 w 之一。工作树在其中一个存储袋中提交。但是git show当它看到 merge 提交时,行为会有所不同。正如文档所说:

It also presents the merge commit in a special format as produced by git diff-tree --cc.



所以git show stash@{1}向您显示“组合差异”,假设提交 w是提交的正常 merge Ci , 生产 w .这毕竟不是正常的 merge ,尽管 merge 的差异实际上可能很有用,前提是您知道自己在看什么。阅读 --cc git diff-tree 下的文档详细了解它是如何工作的,但我会注意到 --cc暗示 -c其中包括这一点:

... lists only files which were modified from all parents.



stash 的情况下, 如果您有 git add -ed 运行前的文件 git stash ,使 i -vs- w diff 为空,您不会在此处的输出中看到这些文件。

最后,如果您git diff stash@{M} stash@{N} : 这只是问git diff比较不同的w ork-tree 提交。这有多少意义,取决于你在比较什么,这通常取决于 stash 袋的连接位置。

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

2“引用名称”只是一个名称,而类似于分支或标签名称。引用名称有多种可能的形式。分支和标签只是具有特殊用途的名称。 “远程分支”是这些引用的另一种形式,“stash”也是一种引用。

事实上,HEAD只是另一个引用,虽然它是一个非常特殊的引用。我很重要,如果你删除 HEAD文件,git 将决定您的存储库毕竟不再是存储库。

除了一些特殊情况— HEAD , ORIG_HEAD , MERGE_HEAD ,等等——所有引用都以字符串 refs/ 开头.分支以 refs/heads/ 开头, 标签以 refs/tags/ 开头,“远程分支”以 refs/remotes/ 开头.换句话说,引用有一个“命名空间”,通常以 refs/ 开头。然后在下面加上另一个词来确定他们住在哪里。

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

3事实上,这确实使用了reflog。这意味着,除其他事项外,除了“主要”存储之外,refs/stash ,遗嘱可以过期。 (幸运的是,作为 musiphil notes ,自 git 1.6.0 以来的默认设置是这些不会过期;您必须为它们配置过期时间才能实现这一点——无论如何,这可能不是您想要的。)

4它使用后缀^的巧妙方法符号,在我的 other answer 中有详细说明.

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

关于git - `git stash show -p stash@{N}` 和 `git show stash@{N}` 之间的区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21738579/

相关文章:

ruby-on-rails - 我如何自制安装gradle 1.12?

git - 如何在 git 中组合多个存储

git log -p 对比 git show 对比 git diff

git - 如何在 Git 中创建仅包含来自特定提交的文件的基线

Git:将现有存储库从 PC 移动到服务器,从服务器克隆

Git:查看我的最后一次提交

git - 带有交互模式的“git stash apply”

git - 您如何判断是否不再需要 git stash?

git - `git show` 本身做了什么?