git stash并申请

时间:2013-12-05 20:20:14

标签: git git-pull git-stash

我是git的新手,并且不太清楚stashing是如何工作的。

假设我正在处理分支主服务器并尝试git pull并收到错误,我的本地更改将被覆盖并需要被隐藏或提交。如果我没有暂停任何更改并运行git stash,请执行git pull并成功更新,当我git stash apply时会发生什么?

一般情况下,如果其他人修改了文件并且我运行了git pull,那么当我run git stash apply时会发生什么?它是否会覆盖刚刚更新的文件,无论它们是否在我存储时被暂存?它是否会覆盖我刚刚使用git pull更新的每个文件,以及存储的文件?

3 个答案:

答案 0 :(得分:62)

Quick" TL; DR"外卖版本,所以人们可以稍后回来学习更多

git stash挂起一个存储包 - 这是一种特殊形式的合并提交,不在当前HEAD提交的任何分支上。稍后的git stash apply,当您在任何提交时 - 可能是不同的提交 - 然后通过查看两者来尝试恢复更改 git计算悬挂的藏匿袋和挂起的承诺。

当您完成更改后,您应该使用git stash drop从提交中删除存储包"存储在"。 (并且,git stash pop只是"申请的简写,然后自动删除"。我建议将这两个步骤分开,以防万一你不喜欢&#34的结果;申请"你想稍后再试。)

长版本

git stash实际上相当复杂。

有人说"git makes much more sense once you understand X",对于许多不同的" X",一般你理解git" git会更有意义。 。 : - )

在这种情况下,要真正了解stash,您需要了解提交,分支,索引/登台区域,git的引用名称空间以及合并的方式所有工作,因为git stash创建了一个非常特殊的合并提交,由通常名称空间之外的名称引用 - 一种奇怪的合并,而不是在分支上#34;在所有 - git stash apply使用git的合并机制来尝试重新应用"在进行特殊合并提交时保存的更改,可选择保留分阶段和非分阶段更改之间的区别。

幸运的是,您实际上并不需要了解所有这些使用 git stash

在这里,您正在处理某个分支(master)并且您还有一些尚未准备好的更改,因此您不想在分支上提交它们。 1 与此同时,其他人提出了一些好的 - 或者至少,你希望它能够很好地进入远程回购中的origin/master,所以你想要选择那些。< / p>

让我们说你和他们都以- A - B - C结尾的提交开始,即C是你开始在分支机构工作时在你的仓库中的最终提交{ {1}}。新的&#34;好的东西&#34;提交,我们会致电masterD

在你的情况下,你正在运行E并且它失败了,而#34;工作目录不干净&#34;问题。所以,你运行git pull。这会以特别奇怪的藏匿方式为您提交您的东西,以便您的工作目录现在干净。现在你可以git stash

就提交的绘制而言(您使用git pullgitk获得的图表),您现在拥有类似的内容。当你运行git log --graph时,隐藏在i-w分支机构中的master悬挂在你的git stash分支机构上的小承包。 (名称iw的原因是这些是&#34; i&#34; ndex / staging-area和&#34; w&#34; ork-tree部分藏。)

- A - B - C - D - E      <-- HEAD=master, origin/master
          |\
          i-w            <-- the "stash"

如果您开始处理master并且从未进行任何提交,则会获得此图。因此,您最近的提交是C。存储后,git pull能够将提交DE添加到您的本地分支master。被隐藏的工作仍悬挂在C上。

如果您做了一些自己的提交 - 我们会将它们称为Y,对于您的提交,Z只是为了进行两次提交 - &#34;藏匿然后拉&#34;看起来像这样:

                   .-------- origin/master
- A - B - C - D - E - M  <-- HEAD=master
            \       /
              Y - Z
                  |\
                  i-w    <-- the "stash"

这一次,stash将其藏匿的行李挂在Z后,pull - 仅fetch然后merge - 必须执行真正的合并,而不仅仅是快速前进&#34;。所以它使提交M成为合并提交。 origin/master标签仍然指代提交E,而不是M。您现在正在提交master M,这是EZ的合并。你提前一步&#34; origin/master

在任何一种情况下,如果您现在运行git stash apply,则存储脚本(它是一个使用大量低级git&#34; plumbing&#34;命令的shell脚本)有效 2 这样做:

git diff stash^ stash > /tmp/patch
git apply /tmp/patch

这会区分stash,其名称为w - &#34;工作树&#34;存储的一部分 - 正确的 3 父级。换句话说,它发现了你改变了什么&#34;正确的父提交(CZ,视情况而定)和隐藏的工作树之间。然后,根据您的开始位置,它会将更改应用于当前签出的版本,即EM

顺便说一句,git stash show -p实际上只运行相同的git diff命令(当然没有> /tmp/patch部分)。如果没有-p,则会使用--stat运行差异。因此,如果您想详细了解git stash apply将合并的内容,请使用git stash show -p。 (这不会告诉你git stash apply可以尝试从隐藏的索引部分应用什么;这是我对存储脚本的一个小抱怨。)


在任何情况下,一旦存储干净地应用,您可以使用git stash drop删除对存储袋的引用,以便它可以被垃圾收集。在你放弃它之前,它有一个名字(refs/stash,又名stash@{0}),因此它会永远保持&#34;永远&#34; ...除了你创建一个 new 藏匿的事实,stash脚本&#34;推动&#34;当前存储到存储reflog中(以使其名称变为stash@{1})并使新存储使用refs/stash名称。大多数reflog条目会持续90天(您可以将其配置为不同)然后过期。默认情况下,stashes不会过期,但如果你另外配置,则推送&#34;&#34;推送&#34;藏匿可能会迷路,所以要小心依赖&#34;永远保存&#34;如果您开始根据自己的喜好配置git。

请注意git stash drop&#34; pops&#34;此处存储堆栈,将stash@{2}重新编号为stash@{1}并使stash@{1}变为普通stash。使用git stash list查看存储堆栈。


1 无论如何,继续提交它们并不坏,然后再做git rebase -i来压缩或修复第二,第三,第四...... ,nth提交,和/或重写临时&#34;检查点&#34;承诺。但这与此无关。

2 它有点复杂,因为你可以使用--index来尝试保持上演阶段的变化,但事实上,如果你查看脚本,你就可以# 39;将看到实际的命令序列git diff ... | git apply --index。在这种情况下,它确实只是应用了差异!但是,它最终会直接调用git merge-recursive来合并工作树,允许从其他地方引入相同的更改。如果您的补丁能够完成“好东西”,那么简单的git apply就会失败。提交DE也可以。

3 这使用git的父命名魔术语法,并在stash脚本中进行一些提前规划。因为存储是这个时髦的合并提交,w有两个甚至三个父母,但是存储脚本设置它以便&#34;第一个父母&#34;是原始提交,CZ,视情况而定。 &#34;第二个父母&#34; stash^2是提交时的索引状态,在小悬挂的存储袋中显示为i,第三个父级&#34;如果存在,则为未分级 - 来自git stash save -ugit stash save -a的可能被忽略的文件。

请注意,在这个答案中,我假设你小心地分阶段工作树的一部分而你 使用git stash apply --index来恢复分阶段索引。如果不执行任何操作,则会使i提交非常冗余,因此我们无需在apply步骤中担心它。如果你 使用apply --index或等价物,并且分阶段的项目,你可以进入更多的角落案例,其中藏匿处不适用干净。

这些相同的警告适用于更多的角落案例,以及使用-u-a保存的第三次提交的藏匿处。

对于这些超级难的案例,git stash提供了一种将存储转变为成熟的分支的方法 - 但我会将所有这些留给另一个答案。< / p>

答案 1 :(得分:3)

stash git命令会记住藏匿的来源:

   git stash list

out put

   stash@{0}: WIP on master.color-rules.0: 35669fb [NEW] another step toward initial cube

在哪里可以看到它在哪个SHA1上制作。因此,如果你git stash,git pull,git stash apply并且你遇到了冲突,那么stash不会被删除(只有当你丢弃或者申请成功时才会被删除)。因此,您始终可以从git存储列表中获取SHA1和

   git checkout 35669fb
   git stash apply

保证工作。我建议使用-b选项并为该恢复提供分支名称。

话虽如此,我最喜欢的工作流程是总是以新的“personnal”名称结账,以避免此类问题

答案 2 :(得分:1)

通常未提交的更改总是很糟糕。你的改变是好的,然后提交它们,或者它们是坏的而不是丢弃它们。在进行未提交的更改时执行任何git操作往往会导致麻烦,git将无法帮助您,因为git不知道您未提交的任何内容。

话虽如此,回到你的问题。 ;)

Git一般都非常聪明。应用您的存储时,它会尝试将您的更改与其他更改合并。大部分时间这都有效。

如果更改确实发生冲突,因为您以不同的方式更改了相同的行,git会告诉您,您必须自己解决冲突。 - 即使在这种情况下,git也会通过git mergetool来帮助您,它会启动一个合适的命令来向您显示冲突并允许您逐个解决它们。