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

mus*_*hil 11 git git-stash git-show

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

$ git stash show -p stash@{N}
Run Code Online (Sandbox Code Playgroud)

$ git show stash@{N}
Run Code Online (Sandbox Code Playgroud)

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

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

我还可以依赖于git diff stash@{M} stash@{N}正确的事情吗?

tor*_*rek 20

藏匿袋

拯救的东西git stash就是我所谓的"藏匿袋".它由两个1不同的提交:"索引"提交(临时区域),而"工作树"承诺.工作树提交是一种有趣的合并提交.

让我在这里再次绘制这个(请参阅更长版本的参考答案),只是为了正确说明它.让我们假设你有一个小的repo只有一个分支和三个提交,A通过C.你在一个分支上做了一些改变然后运行git stash save(或者只是简单git stash).这就是你得到的:

A - B - C     <-- HEAD=master
        |\
        i-w   <-- the "stash"
Run Code Online (Sandbox Code Playgroud)

现在你可以制作(或切换到)另一个分支,但是为了说明,我们只是说你把那个存储留在那里并进行更多的"常规"提交master:

A - B - C - D - E    <-- HEAD=master
        |\
        i-w   <-- stash
Run Code Online (Sandbox Code Playgroud)

这里的要点是"stash-bag",即index和work-tree提交对,仍然像以前一样挂起.提交无法更改,对于stash-bag提交也是如此.

但是现在你通过进行一些更改(仍在继续)并再次运行来创建新的存储.mastergit stash save

旧的藏匿袋怎么了?"参考名称" 2 stash,现在指向新的藏匿袋.但旧的存储袋提交仍在那里.他们现在需要一个"reflog"样式名称stash@{1}.3

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

A - B - C - D - E     <-- HEAD=master
        |\      |\
        i-w     i-w   <-- stash
          .
           -------------- stash@{1}
Run Code Online (Sandbox Code Playgroud)

(当你使用时git stash drop,存储脚本只是操作stashref 的reflog 来删除被删除的存储包的ID.这就是所有"更高"的存储包重新编号的原因.实际的存储包本身是垃圾收集在下一个git gc. )

下一步是了解正在发生的事情的关键.

任何时候git都需要你命名一个特定的提交,你可以通过许多不同的方式来做.

每个提交都有一个"真实的名字",这是你看到的丑陋的SHA-1哈希,值如676699a0e0cdfd97521f3524c763222f1c30a094.你可以这样写.它总是意味着相同的提交.提交永远不会被更改,并且这是提交的整个内容的加密哈希,因此如果存在该特定提交,则该值始终是其名称.

不过,这对人们来说不是一个好名字.因此,我们有别名:之类的东西分支和标签名称,像相对名称HEADHEAD~2,并引用日志式的名字,如HEAD@{yesterday}master@{1}.(有一个命令,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 stash show,git show和git diff

好的,最后,我们可以到你的git showvs git stash show,git diff依此类推.让我们git stash show首先解决,因为这是你应该用于存储的那个.此外,git stash子命令将验证您命名的提交 - 或者,如果您没有提交提交,通过stash引用找到的提交 - "看起来像"一个存储,即,这些有趣的合并提交之一.

如果你运行git stash show -p,git会显示diff(-patch).但究竟是什么表现出来的呢?

用藏匿袋回到图表.每个存储包都挂起一个特定的提交.上面,"主要"藏匿现在悬挂在提交中E,而stash@{1}早期的藏匿处正悬挂在上面C.

什么git stash show -p是比较存储的工作树提交,w以及存储挂起的提交.4

你当然可以自己做.假设你想比较挂起提交的win 和commit ,它可以由branch-name命名.所以你可以运行:.这里的名称是指(当前)存储提交,并引用提交,因此这将生成完全相同的补丁.(并且,如果你想比较in 和commit ,你只需要运行这样你就可以命名那两个提交.当然,它更容易.)5stashEEmastergit diff master stashstashwmasterEgit stash show -p stashwstash@{1}Cgit diffgit stash show -p stash@{1}

平原git show怎么样?这有点复杂. git show很高兴显示一个提交,你给它一个stash引用(stash本身,或其中一个reflog变种).这是一个有效的提交标识符,它解析为其中w一个存储袋中的工作树提交之一.但是git show当它看到合并提交时,行为会有所不同.正如文件所说:

它还以特殊格式呈现合并提交git diff-tree --cc.

因此git show stash@{1},假设提交w是提交Ci生成的正常合并,则向您显示"组合差异" w.毕竟这不是一个正常的合并,虽然组合差异可能实际上是有用的,只要你知道你在看什么.阅读下面的--cc文档git diff-tree,看看它是如何工作的,但我会注意到这包括这个--cc含义-c:

...仅列出从所有父母修改的文件.

对于a stash,如果你git add在运行之前已经有了-ed文件git stash,那么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 "引用名称"只是一个名称,而不是分支或标记名称.有许多可能的参考名称形式.分支和标签只是具有特殊用途的名称."远程分支"是这些引用的另一种形式,"存储"也是引用.

事实上,HEAD它只是另一个参考,虽然它是一个非常特殊的参考.我非常重要,如果您删除该HEAD文件,git将决定您的存储库不再是存储库.

对于某些特殊情况者除外HEAD,ORIG_HEAD,MERGE_HEAD,等引用都开始用字符串refs/.分支以字母开头refs/heads/,标签以refs/tags/"远程分支"开头refs/remotes/.换句话说,引用具有"名称空间",通常从其开始,refs/然后在其下面获得另一个单词以识别它们的居住地.

存储参考拼写refs/stash(并停在那里,没有refs/stash/jimmy_kimmel或类似的东西).

3其实,这真的使用引用日志.这意味着,除了"主要"之外的其他东西refs/stash,将会过期.(幸运的是,正如musiphil所说,自git 1.6.0以来的默认值是它们不会过期;你必须为它们配置到期时间才能实现这一点 - 这可能不是你想要的.)

4在我的另一个答案中,使用后缀表示法巧妙使用后缀^表示法.

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

  • 这是一个非常详细的答案.感谢您抽出宝贵的时间. (5认同)