为什么git stash pop表示无法从存储条目恢复未跟踪的文件? [英] Why does git stash pop say that it could not restore untracked files from stash entry?

查看:1194
本文介绍了为什么git stash pop表示无法从存储条目恢复未跟踪的文件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我进行了许多上,下阶段的更改,我想快速切换到另一个分支,然后再切换回去.

I had a bunch of staged and unstaged changes and I wanted to quickly switch to another branch and then switch back.

因此,我使用以下命令进行了更改:

So I staged my changes using:

$ git stash push -a

(事后看来,我本可以使用--include-untracked代替--all)

(In hindsight I probably could have used --include-untracked instead of --all)

然后,当我弹出储藏柜时,我遇到了很多错误:

Then when I went to pop the stash I get a whole lot of errors along the lines of:

$ git stash pop
foo.txt already exists, no checkout
bar.txt already exists, no checkout
...
Could not restore untracked files from stash entry

似乎没有从存储中恢复任何更改.

There doesn't seem to be any changes restored from the stash.

我也尝试了$ git stash branch temp,但是显示了相同的错误.

I also tried $ git stash branch temp but that shows the same errors.

我确实找到了解决该问题的方法:

I did figure out a way around this which was to use:

$ git stash show -p | git apply

目前已经避免了灾难,但这引发了一些问题.

Disaster averted for now but this raises some questions.

为什么首先会发生此错误,下次如何避免呢?

Why did this error happen in the first place and how do I avoid it next time?

推荐答案

作为其他说明,请注意git stash进行两次提交或三次提交.默认值为2;默认值为2.如果使用--all--include-untracked选项的任何拼写,您将获得三个.

As a bit of additional explanation, note that git stash makes either two commits, or three commits. The default is two; you get three if you use any spelling of the --all or --include-untracked options.

这两个或三个提交在一种重要的方式上很特殊:它们位于 no 分支上. Git通过特殊名称stash定位它们. 1 不过,最重要的是,Git允许您(和使制造)使用这两个或三个提交.要了解这一点,我们需要查看这些提交中的内容.

These two, or three, commits are special in one important way: they are on no branch. Git locates them through the special name stash.1 The most important thing, though, is what Git lets you—and makes you—do with these two or three commits. To understand this we need to look at what's in those commits.

每个提交都可以列出一个或多个提交.它们形成一个图,以后的提交指向以前的提交.该存储区通常包含两个提交,我喜欢将i用作索引/暂存区域的内容,并将w称为工作树的内容.还要记住,每个提交都包含一个快照.在正常提交中,此快照是从索引/暂存区内容中 创建的.因此,i提交实际上是一个完全正常的提交!它只是不在任何分支上:

Every commit can list one or more parent commits. These form a graph, where later commits point back to earlier ones. The stash normally holds two commits, which I like to call i for the index / staging-area contents, and w for the work-tree contents. Remember also that each commit holds a snapshot. In a normal commit, this snapshot is made from the index / staging-area contents. So the i commit is in fact a perfectly normal commit! It's just not on any branch:

...--o--o--o   <-- branch (HEAD)
           |
           i

如果要进行常规存储,git stash代码现在通过将所有跟踪的工作树文件(复制到临时辅助索引)复制为w. Git将此w提交的第一个父级设置为指向HEAD提交,第二个父级将指向提交i.最后,它设置stash指向此w提交:

If you're making a normal stash, the git stash code makes w now by copying all your tracked work-tree files (into a temporary auxiliary index). Git sets the first parent of this w commit to point to the HEAD commit, and the second parent to point to commit i. Last, it sets stash to point to this w commit:

...--o--o--o   <-- branch (HEAD)
           |\
           i-w   <-- stash

如果添加--include-untracked--all,则Git会在创建iw之间进行一次额外的提交u. u的快照内容是那些未被跟踪但未被忽略的文件(--include-untracked),或者是即使被忽略也未被跟踪的文件(--all).此额外的u提交具有 no 父级,然后当git stash生成w时,它将w third 父级设置为此u提交,这样您就可以得到:

If you add --include-untracked or --all, Git makes an extra commit, u, in between making i and w. The snapshot contents for u are those files that are untracked but not ignored (--include-untracked), or files that are untracked even if they are ignored (--all). This extra u commit has no parent, and then when git stash makes w, it sets w's third parent to this u commit, so that you get:

...--o--o--o   <-- branch (HEAD)
           |\
           i-w   <-- stash
            /
           u

这时,Git还删除u提交中结束的所有工作树文件(使用git clean进行此操作).

Git also, at this point, removes any work-tree files that wound up in the u commit (using git clean to do that).

还原存储区时,可以选择使用--index,也可以不使用它.这告诉git stash apply(或内部使用apply的任何命令,例如pop)应使用 i提交以尝试修改当前索引.此修改是通过以下方式完成的:

When you go to restore a stash, you have the option of using --index, or not using it. This tells git stash apply (or any of the commands that internally use apply, such as pop) that it should use the i commit to attempt to modify your current index. This modification is done with:

git diff <hash-of-i> <hash-of-i's-parent> | git apply --index

(或多或少;这里有很多细微的细节妨碍了基本思想).

(more or less; there are a bunch of nitty details that get in the way of the basic idea here).

如果省略--index,则git stash apply会完全忽略i提交.

If you omit --index, git stash apply completely ignores the i commit.

如果存储区只有两个提交,则git stash apply现在可以应用w提交.它使用生成存储的原始提交(i的父级,通过调用git merge 2 (不允许将其提交或将结果视为普通合并)来完成此操作和w的第一个父级)作为合并基础,w作为--theirs提交,而您当前的(HEAD)提交作为合并的目标.如果合并成功,那么一切都很好-至少 Git 如此认为-并且git stash apply本身也会成功.如果您使用git stash pop来应用存储,则代码现在会删除存储. 3 如果合并失败,Git会声明应用失败.如果您使用的是git stash pop,则代码将保留存储并提供与git stash apply相同的故障状态.

If the stash has only two commits, git stash apply can now apply the w commit. It does this by calling git merge2 (without allowing it to commit or treat the result as a normal merge), using the original commit on which the stash was made (i's parent, and w's first parent) as the merge base, w as the --theirs commit, and your current (HEAD) commit as the target of the merge. If the merge succeeds, all is good—well, at least Git thinks so—and the git stash apply itself succeeds. If you used git stash pop to apply the stash, the code now drops the stash.3 If the merge fails, Git declares the apply to have failed. If you used git stash pop, the code retains the stash and delivers the same failure status as for git stash apply.

但是,如果您具有该 third 提交-如果您正在应用的存储中包含一个u提交-则情况会改变! 无法假装u提交不存在. 4 Git坚持要从 提取所有u文件提交到当前工作树中.这意味着文件要么根本不存在,要么内容与u提交中的内容相同.

But if you have that third commit—if there is a u commit in the stash you are applying—then things change! There is no option to pretend that the u commit does not exist.4 Git insists on extracting all the files from that u commit, into the current work-tree. This means the files must either not exist at all, or have the same contents as in the u commit.

要实现这一目标,您可以自己使用git clean,但是请记住,未跟踪的文件(是否被忽略)在Git存储库中没有其他存在,因此请确保这些文件都可以被销毁!或者,您可以创建一个临时目录,然后将文件移动到该目录中以进行保管-甚至执行另一个git stash save -ugit stash save -a,因为这些文件将为您运行git clean.但这只会给您留下另一个u样式的隐藏物,可供以后处理.

To make that happen, you can use git clean yourself—but remember that untracked files (ignored or not) have no other existence inside a Git repository, so be sure these files can all be destroyed! Or, you can make a temporary directory, and move the files there for safekeeping—or even do another git stash save -u or git stash save -a, since those will run git clean for you. But that just leaves you with another u-style stash to deal with later.

1 这实际上是refs/stash.如果您创建一个名为stash的分支,这一点很重要:分支的全名是refs/heads/stash,因此它们没有冲突.但是不要那样做: Git 不会介意,但是您会迷惑自己. :-)

1This is in fact refs/stash. This matters if you make a branch named stash: the branch's full name is refs/heads/stash, so these are not in conflict. But don't do that: Git won't mind, but you will confuse yourself. :-)

2 git stash代码实际上直接在这里使用git merge-recursive.出于多种原因,这是必要的,并且还具有确保在解决冲突和提交时Git不会将其视为合并的副作用.

2The git stash code actually uses git merge-recursive directly here. This is necessary for multiple reasons, and also has the side effect of making sure Git does not treat it as a merge when you resolve conflicts and commit.

3 这就是为什么我建议避免使用git stash pop,而推荐使用git stash apply的原因.您将有机会查看应用了哪些内容,并确定它是否实际上正确地应用了.如果没有,那么您仍然有藏匿处,这意味着您可以使用git stash branch完美地恢复所有内容.好吧,假设缺少令人讨厌的u提交.

3This is why I recommend avoiding git stash pop, in favor of git stash apply. You get a chance to review what got applied, and decide whether it was actually applied correctly. If not, you still have your stash which means you can use git stash branch to recover everything perfectly. Well, assuming the lack of that pesky u commit.

4 确实应该是:git stash apply --skip-untracked之类的.还应该有一个变体,意味着将所有这些u提交文件拖放到新目录中,例如git stash apply --untracked-into <dir>.

4There really should be: git stash apply --skip-untracked or something. There should also be a variant that means drop all those u commit files into a new directory, e.g., git stash apply --untracked-into <dir>, perhaps.

这篇关于为什么git stash pop表示无法从存储条目恢复未跟踪的文件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆