代码之家  ›  专栏  ›  技术社区  ›  steinybot

为什么git stash pop说它不能从stash条目恢复未跟踪的文件?

  •  17
  • steinybot  · 技术社区  · 6 年前

    我有很多阶段性和非阶段性的变化,我想迅速切换到另一个分支,然后切换回。

    因此,我使用以下方法进行了更改:

    $ git stash push -a
    

    (事后看来,我可能会用 --include-untracked 而不是 --all )

    当我去打开储藏室的时候,我得到了一大堆的错误:

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

    似乎没有任何变化从储藏室恢复。

    我也试过了 $ git stash branch temp 但这也显示出同样的错误。

    我想了一个办法来解决这个问题:

    $ git stash show -p | git apply
    

    灾难暂时得以避免,但这引发了一些问题。

    为什么这个错误一开始就发生了,下次怎么避免呢?

    3 回复  |  直到 6 年前
        1
  •  35
  •   torek    6 年前

    作为一个额外的解释,请注意 git stash 要么两次提交,要么三次提交。默认值是2;如果使用 --all --include-untracked 选项。

    这两个或三个提交在一个重要方面是特殊的:它们在 分支。git通过特殊的名称定位它们 stash 是的。 不过,最重要的是git允许您 使 你要做这两三件事。为了理解这一点,我们需要看看这些承诺中的内容。

    藏在里面的东西

    每个提交都可以列出一个或多个 起源 承诺。它们形成一个图形,在该图形中,后面的提交指向前面的提交。这个存储库通常包含两个提交,我喜欢称之为 i 对于索引/暂存区域内容,以及 w 对于工作树内容。还要记住,每次提交都保存一个快照。在正常提交中,将生成此快照 索引/暂存区域内容。所以 提交实际上是一个完全正常的提交!它不在任何分支上:

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

    如果你在做一个普通的储藏室, 暂存 代码生成 西 现在通过复制所有跟踪的工作树文件(成为临时辅助索引)。git设置此 西 承诺指向 HEAD 提交,以及指向提交的第二个父级 是的。最后,它设定 指向这个 西 提交:

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

    如果你加上 --包括未跟踪的 --全部 ,git进行额外的提交, u ,中间制作 西 是的。的快照内容 U型 是那些未跟踪但未被忽略的文件吗( --包括未跟踪的 )或未跟踪的文件,即使它们被忽略( --全部 )中。这个额外的 U型 提交具有 父母,然后当 暂存 使 西 ,它设置 西 第三的 此项的父项 U型 承诺,让你得到:

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

    同时,在这一点上, 删除 任何工作树文件 U型 提交(使用 git clean 这样做)。

    恢复藏物

    当你去 恢复 一个储藏室,你可以选择使用 --index ,或者不使用它。这说明 git stash apply (或内部使用的任何命令) apply ,例如 pop )它应该 使用 这个 提交以尝试修改当前索引。此修改通过以下方式完成:

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

    (或多或少;这里有一大堆琐碎的细节妨碍了基本的想法)。

    如果你忽略了 --索引 我是说, git stash应用 完全忽略 承诺。

    如果仓库只有两次提交, git stash应用 现在可以应用 西 承诺。它通过打电话 git merge 2个 (不允许提交或将结果视为正常合并)使用进行存储的原始提交( 的父级,以及 西 的第一个父对象)作为合并基, 西 作为 --theirs 提交,并将当前(head)提交作为合并的目标。如果合并成功,至少一切都很好 吉特 索恩认为 git stash应用 自己成功了。如果你用 git stash pop 要应用存储,现在的代码 储藏室。 如果合并失败,git会声明应用程序失败。如果你用 吉特藏酒 ,代码保留存储并传递与 git stash应用 是的。

    但如果你有 第三的 如果有 U型 在你申请的藏匿处承诺-然后事情就变了! 没有办法假装 U型 提交不存在。 git坚持提取所有文件 那个 U型 提交到当前工作树中。这意味着文件必须根本不存在,或者具有相同的内容。 U型 承诺。

    要做到这一点,你可以使用 吉特清洁 记住,未跟踪的文件(忽略或不存在)在Git存储库中没有其他存在,所以确保这些文件都可以被销毁!或者,您可以创建一个临时目录,并将文件移到那里进行安全保存,甚至执行其他操作 git stash save -u git stash save -a ,因为它们将运行 吉特清洁 为你。但那只会给你留下另一个 U型 -以后要处理的样式存储。


    事实上 refs/stash 是的。如果你把一个分支命名为 :分行全名为 refs/heads/stash ,所以这些没有冲突。但不要这样做: 吉特 不介意,但你会搞糊涂的。:-)

    2个 这个 暂存 代码实际使用 git merge-recursive 就在这里。这是出于多种原因所必需的,而且还有一个副作用,就是在解决冲突和提交时,确保git不会将其视为合并。

    这就是为什么我建议避免 吉特藏酒 ,有利于 git stash应用 是的。你有机会回顾一下申请的内容,然后决定是否 事实上 应用正确。如果不是,你 还藏着你的东西 也就是说你可以用 git stash branch 完美地恢复一切。好吧,假设没有那个讨厌的 U型 承诺。

    4个 真的应该有: git stash apply --skip-untracked 或者别的什么。也应该有一个变体,意思是 放下那些 U型 将文件提交到新目录 ,例如, git stash apply --untracked-into <dir> ,也许吧。

        2
  •  46
  •   Daniel Smith    6 年前

    我设法重现了你的问题。如果你保存未跟踪的文件,然后创建这些文件(在你的示例中, foo.txt bar.txt ,则对未跟踪的文件进行本地更改,应用时将覆盖这些更改。 git stash pop 是的。

    要解决此问题,可以使用以下命令:

    git checkout stash -- .
    

    这将覆盖所有未保存的本地更改,因此请小心。 Here is some further information I found on the previous command 是的。

        3
  •  6
  •   Matthew Sielski    5 年前

    展开 Daniel Smith's answer :该代码只恢复 跟踪 文件,即使你使用 --include-untracked (或 -u )当创建存储库时。所需的完整代码为:

    git checkout stash -- .
    git checkout stash^3 -- .
    git stash drop
    
    # Optional to unstage the changes (auto-staged by default).
    git reset
    

    这将完全恢复跟踪的内容(在 stash )以及未追踪的内容(在 stash^3 ),然后删除存储。几点注意事项

    • 小心 -这将覆盖所有与您的隐藏内容!
    • 使用还原文件 git checkout 使它们自动变成舞台,所以我添加了 git reset 把一切都拆开。
    • 一些资源使用 stash@{0} stash@{0}^3 ,在我的测试中,不管有没有 @{0}

    资料来源: