git stash 并申请
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/20409853/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
git stash and apply
提问by neridaj
I'm new to git and not quite clear on how stashing works.
我是 git 的新手,不太清楚存储的工作原理。
Let's say I'm working on branch master and try to git pull
and receive the error that my local changes would be overwritten and need to be stashed or committed. If I haven't staged any of my changes and run git stash
, then do a git pull
and and update successfully, what happens when I git stash apply
?
假设我在分支 master 上工作并尝试git pull
并收到错误,即我的本地更改将被覆盖并且需要隐藏或提交。如果我没有暂存任何更改并运行git stash
,然后执行git pull
并成功更新,那么会发生什么git stash apply
?
In general, If someone else modifies files and I run git pull
, what happens when I run git stash apply
? does it overwrite the files that were just updated, regardless of whether or not they were staged when I stashed them? Does it overwrite every file that I just updated with git pull
, with the files that were stashed?
一般来说,如果其他人修改文件而我运行git pull
,当我运行时会发生什么run git stash apply
?它是否会覆盖刚刚更新的文件,无论它们在我藏匿它们时是否已上演?它是否会覆盖我刚刚更新的每个文件git pull
,以及隐藏的文件?
回答by torek
Quick "TL;DR" take-away version, so one can come back later and study more
快速的“TL;DR”外卖版,所以以后可以回来学习更多
git stash
hangs a stash-bag—this is a peculiar form of a merge commit that is not on any branch—on the current HEAD
commit. A later git stash apply
, when you're at any commit—probably a differentcommit—then tries to restore the changesgit computes by looking at both the hanging stash-bag and the commit it hangs from.
git stash
在当前HEAD
提交上挂起一个 stash-bag——这是一种特殊形式的合并提交,它不在任何分支上。稍后git stash apply
,当您进行任何提交时(可能是不同的提交),然后尝试通过查看挂起的 stash-bag 和它挂起的提交来恢复git 计算的更改。
When you're done with the changes, you should use git stash drop
to let go of the stash-bag from the commit it was "stashed on". (And, git stash pop
is just shorthand for "apply, then automatically drop". I recommend keeping the two steps separate, though, in case you don't like the result of "apply" and you want to try again later.)
当您完成更改后,您应该使用git stash drop
它从“藏匿”的提交中释放藏匿袋。(而且,git stash pop
只是“应用,然后自动删除”的简写。不过,我建议将两个步骤分开,以防万一您不喜欢“应用”的结果并且想稍后再试。)
The long version
长版
git stash
is actually fairly complex.
git stash
实际上相当复杂。
It's been said that "git makes much more sense once you understand X", for many different values of "X", which generalizes to "git makes much more sense once you understand git". :-)
据说“一旦你理解了 X,git 就更有意义”,对于许多不同的“X”值,这概括为“一旦你理解了 git,git 就会更有意义”。:-)
In this case, to reallyunderstand stash
, you need to understand how commits, branches, the index/staging-area, git's reference name space, and merges all work, because git stash
creates a very peculiar merge commit that is referred-to by a name outside the usual name-spaces—a weird kind of merge that is not "on a branch" at all—and git stash apply
uses git's merge machinery to attempt to "re-apply" the changes saved when the peculiar merge commit was made, optionally preserving the distinction between staged and unstaged changes.
在这种情况下,要真正理解stash
,您需要了解提交、分支、索引/暂存区、git 的引用名称空间和合并所有工作是如何工作的,因为git stash
创建了一个非常奇特的合并提交,该提交由外部名称引用通常的命名空间——一种根本不在“分支上”的奇怪合并——并git stash apply
使用 git 的合并机制来尝试“重新应用”在进行特殊合并提交时保存的更改,可选择保留区别阶段性和非阶段性变化之间。
Fortunately, you don't actually need to understand all of that to usegit stash
.
幸运的是,您实际上并不需要了解所有这些才能使用git stash
.
Here, you're working on some branch (master
) and you have some changes that aren't ready yet, so you don't want to commit them on the branch.1Meanwhile someone else put something good—or at least, you hope it's good—into the origin/master
over on the remote repo, so you want to pick those up.
在这里,您正在某个分支 ( master
)上工作,并且您有一些尚未准备好的更改,因此您不想在该分支上提交它们。1与此同时,其他人把一些好的东西——或者至少,你希望它是好的——放到origin/master
远程仓库的上方,所以你想把它们捡起来。
Let's say that you and they both started with commits that end in - A - B - C
, i.e., C
is the final commit that you had in your repo when you started working on branch master
. The new "something good" commits, we'll call D
and E
.
假设您和他们都以以 结尾的提交开始- A - B - C
,即,C
当您开始在 branch 上工作时,这是您在存储库中的最终提交master
。新的“好东西”提交,我们将调用D
和E
。
In your case you're running git pull
and it fails with the "working directory not clean" problem. So, you run git stash
. This commits your stuff for you, in its special weird stash-y fashion, so that your working directory is now clean. Now you can git pull
.
在您的情况下,您正在运行git pull
并且它因“工作目录不干净”问题而失败。所以,你跑git stash
。这会以它特殊的奇怪的 stash-y 方式为你提交你的东西,所以你的工作目录现在是干净的。现在你可以了git pull
。
In terms of drawing of commits (a graph like you get with gitk
or git log --graph
), you now have something like this. The stash is the little bag-of-i-w
dangling off the commit you were "on", in your master
branch, when you ran git stash
. (The reason for the names i
and w
is that these are the "i"ndex / staging-area and "w"ork-tree parts of the stash.)
在绘制提交方面(类似于您使用gitk
或获得的图形git log --graph
),您现在有这样的东西。stash 是当您运行时i-w
在您的master
分支中“打开”的提交中悬挂的小袋子git stash
。(原因的名字i
和w
是,这是“我” ndex /舞台区和“W”藏匿的扫树的部分。)
- A - B - C - D - E <-- HEAD=master, origin/master
|\
i-w <-- the "stash"
This drawing is what you get if you started working on master
and never did anycommits. The most recent commit you had was thus C
. After making the stash, git pull
was able to add commits D
and E
to your local branch master
. The stashed bag of work is still hanging off C
.
如果您开始工作master
并且从未进行任何提交,则此图是您所得到的。因此,您最近的提交是C
. 使藏匿后,git pull
才得以补充提交D
,并E
以当地分行master
。藏匿的工作袋仍然悬而未决C
。
If you made a few commits of your own—we'll call them Y
, for your commit, and Z
just to have two commits—the result of the "stash then pull" looks like this:
如果你自己做了几次提交——我们将它们称为Y
,用于你的提交,并且Z
只是为了有两次提交——“stash then pull”的结果如下所示:
.-------- origin/master
- A - B - C - D - E - M <-- HEAD=master
\ /
Y - Z
|\
i-w <-- the "stash"
This time, after stash
hung its stash-bag off Z
, the pull
—which is just fetch
then merge
—had to do a real merge, instead of just a "fast forward". So it makes commit M
, the merge commit. The origin/master
label still refers to commit E
, not M
. You're now on master
at commit M
, which is a merge of E
and Z
. You're "one ahead" of origin/master
.
这一次,在stash
挂掉它的藏匿袋之后Z
,pull
就在fetch
那时merge
——必须进行真正的合并,而不仅仅是“快进”。所以它使 commit M
,合并提交。该origin/master
标签仍然是指犯E
,没有M
。您现在master
处于 commit 状态M
,它是E
and的合并Z
。你“领先”了origin/master
.
In either case, if you now run git stash apply
, the stash script (it's a shell script that uses a lot of low level git "plumbing" commands) effectively2does this:
在任一情况下,如果您现在运行git stash apply
stash 脚本(它是一个使用大量低级 git“管道”命令的 shell 脚本),则有效地2执行以下操作:
git diff stash^ stash > /tmp/patch
git apply /tmp/patch
This diffs stash
, which names w
—the "work tree" part of the stash—against the correct3parent. In other words, it finds out "what you changed" between the proper parent commit (C
or Z
, as appropriate) and the stashed work-tree. It then applies the changes to the currently-checked-out version, which is either E
or M
, again depending on where you started.
这 diffs stash
,它命名w
- 存储的“工作树”部分 - 反对正确的3父。换句话说,它会找出适当的父提交(C
或Z
,视情况而定)和隐藏的工作树之间的“您更改了什么” 。然后它将更改应用到当前签出的版本,即E
或M
,同样取决于您从哪里开始。
Incidentally, git stash show -p
really just runs that same git diff
command (with no > /tmp/patch
part of course). Without -p
, it runs the diff with --stat
. So if you want to see in detail what git stash apply
will merge in, use git stash show -p
. (This won't show you what git stash apply
can attempt to apply from the index part of the stash, though; this is a minor gripe I have with the stash script.)
顺便说一句,git stash show -p
实际上只是运行相同的git diff
命令(> /tmp/patch
当然没有部分)。没有-p
,它会运行 diff --stat
。因此,如果您想详细查看git stash apply
将合并的内容,请使用git stash show -p
. (不过,这不会向您展示git stash apply
可以尝试从 stash 的索引部分应用什么;这是我对 stash 脚本的一个小抱怨。)
In any case, once the stash applies cleanly, you can use git stash drop
to remove the reference to the stash-bag, so that it can be garbage-collected. Until you drop it, it has a name (refs/stash
, aka stash@{0}
) so it sticks around "forever" ... except for the fact that if you make a newstash, the stash
script "pushes" the current stash into the stash reflog (so that its name becomes stash@{1}
) and makes the new stash use the refs/stash
name. Most reflog entries stick around for 90 days (you can configure this to be different) and then expire. Stashes don't expire by default, but if you configure this otherwise, a "pushed" stash can get lost, so be careful about depending on "save forever" if you start configuring git to your liking.
在任何情况下,一旦 stash 应用干净,您可以使用git stash drop
删除对 stash-bag 的引用,以便它可以被垃圾收集。直到你放弃它,它有一个名字(refs/stash
又名stash@{0}
),因此它支左右“永远” ......除了一个事实,即如果你犯了一个新的藏匿处,将stash
剧本“推”目前藏匿到藏匿引用日志(使它的名称变为stash@{1}
) 并使新的存储使用该refs/stash
名称。大多数 reflog 条目会保留 90 天(您可以将其配置为不同的)然后过期。默认情况下,Stash 不会过期,但是如果您以其他方式配置它,“推送”的 stash 可能会丢失,因此如果您开始根据自己的喜好配置 git,请注意依赖“永远保存”。
Note that git stash drop
"pops" the stash stack here, renumbering stash@{2}
to stash@{1}
and making stash@{1}
become plain stash
. Use git stash list
to see the stash-stack.
请注意,git stash drop
此处“弹出”存储堆栈,重新编号stash@{2}
为stash@{1}
并使其stash@{1}
变为普通stash
。使用git stash list
看藏匿堆栈。
1It's not bad to go ahead and commit them anyway, and then do a later git rebase -i
to squash or fixup further second, third, fourth, ..., nth commits, and/or rewrite the temporary "checkpoint" commit. But that's independent of this.
1无论如何,继续提交它们并不坏,然后稍后git rebase -i
再压缩或修复第二次,第三次,第四次,...,第 n 次提交,和/或重写临时“检查点”提交。但这与此无关。
2It's a fair bit more complex because you can use --index
to try to keep staged changes staged, but in fact, if you look in the script, you'll see the actual command sequence git diff ... | git apply --index
. It really does just apply a diff, in this case! Eventually it invokes git merge-recursive
directly, though, to merge in the work tree, allowing the same changes to have been brought in from elsewhere. A plain git apply
would fail if your patch does something the "good stuff" commits D
and E
also does.
2这有点复杂,因为您可以使用--index
来尝试保持暂存的更改暂存,但实际上,如果您查看脚本,您将看到实际的命令序列git diff ... | git apply --index
。在这种情况下,它确实只是应用了差异!git merge-recursive
不过,最终它会直接调用以合并到工作树中,从而允许从其他地方引入相同的更改。git apply
如果你的补丁做了一些“好东西”提交D
并且E
也做的事情,那么平原就会失败。
3This uses git's parent-naming magic syntax, with a little advance planning inside the stash
script. Because the stash is this funky merge commit, w
has two or even three parents, but the stash script sets it up so that the "first parent" is the original commit, C
or Z
, as appropriate. The "second parent" stash^2
is the index state at the time of the commit, shown as i
in the little hanging stash-bag, and the "third parent", if it exists, is unstaged-and-maybe-ignored files, from git stash save -u
or git stash save -a
.
3这使用了 git 的父命名魔法语法,并在stash
脚本内部进行了一些预先规划。因为 stash 是这个时髦的合并提交,w
有两个甚至三个父级,但是 stash 脚本将其设置为“第一个父级”是原始提交,C
或者Z
,视情况而定。“第二个父级”stash^2
是提交时的索引状态,如i
小挂起的储藏袋所示,而“第三个父级”(如果存在)是未暂存且可能被忽略的文件,来自git stash save -u
或git stash save -a
。
Note that I assume, in this answer, that you have notcarefully staged part of your work-tree and that you are notusing git stash apply --index
to restore the staged index. By not doing any of this, you render the i
commit pretty much redundant, so that we need not worry about it during the apply
step. If you areusing apply --index
or equivalent, and havestaged items, you can get into a lot more corner cases, where the stash won't apply cleanly.
请注意,我认为,在这个答案,你有没有仔细上演你的工作树的一部分,你不能使用git stash apply --index
恢复上演指数。通过不执行任何这些操作,您会使i
提交变得非常多余,因此我们无需在此apply
步骤中担心它。如果您正在使用apply --index
或等效,并且有暂存项目,您可以进入更多的角落案例,其中存储不会干净地应用。
These same caveats apply, with yet more corner cases, to stashes saved with -u
or -a
, that have that third commit.
这些相同的警告适用于更多的极端情况,适用于使用-u
或保存的存储-a
,具有第三次提交。
For these extra-hard cases, git stash
provides a way to turn a stash into a full-fledged branch—but I'll leave all that to another answer.
对于这些非常困难的情况,git stash
提供了一种将藏匿处变成成熟分支的方法——但我会将所有这些留给另一个答案。
回答by Sebastien
the stash git command remembers where the stash comes from:
stash git 命令会记住 stash 的来源:
git stash list
out put
输出
stash@{0}: WIP on master.color-rules.0: 35669fb [NEW] another step toward initial cube
Where you can see on which SHA1 it was made on. So if you git stash, git pull, git stash apply and you got a conflict, the stash is not dropped (it will only if you drop or if the apply was successful). So you can always get the SHA1 from git stash list and
您可以在哪里看到它是在哪个 SHA1 上制作的。因此,如果您 git stash、git pull、git stash apply 并且遇到冲突,则不会删除 stash(只有在您删除或应用成功时才会删除)。所以你总是可以从 git stash 列表中获取 SHA1 和
git checkout 35669fb
git stash apply
and it is garanteed to work. I recommend using the -b option and provide a branch name for that recovery.
它保证工作。我建议使用 -b 选项并为该恢复提供一个分支名称。
That being said, my favorite workflow is to ALWAYS checkout under on new "personnal" name to avoid such problems
话虽如此,我最喜欢的工作流程是始终以新的“个人”名称结帐以避免此类问题
回答by michas
Generally uncommitted changes are always bad. Either your changes are good, then commit them, or they are bad than discard them. Doing any git operations while having uncommitted changes tends to cause trouble and git will not be able to help you, as git does not know about anything you did not commit.
通常未提交的更改总是不好的。要么你的改变是好的,然后提交它们,要么是坏的,而不是丢弃它们。在未提交的更改的同时执行任何 git 操作往往会导致麻烦并且 git 将无法帮助您,因为 git 不知道您未提交的任何内容。
Having said that, back to your question. ;)
说了这么多,回到你的问题。;)
Git is generally pretty smart. When you apply your stash, it tries to merge your changes with the other changes. Most of the time this just works.
Git通常非常聪明。当您应用存储时,它会尝试将您的更改与其他更改合并。大多数时候这只是有效。
If the changes really conflict, because you changed the same lines in a different way, git will tell you, and you will have to resolve the conflict by yourself. - Even in this case git will help you by having git mergetool
, which will launch a suitable command to show you the conflicts and allows you to resolve them one by one.
如果更改确实发生冲突,因为您以不同的方式更改了相同的行,git 会告诉您,您必须自己解决冲突。- 即使在这种情况下,git 也会通过 have 帮助您git mergetool
,它将启动一个合适的命令来向您显示冲突并允许您一一解决它们。