【问题标题】:How would I extract a single file (or changes to a file) from a git stash?如何从 git stash 中提取单个文件(或对文件的更改)?
【发布时间】:2010-11-09 11:13:18
【问题描述】:

是否可以从 git stash 中提取单个文件或文件的 diff 而不会弹出 stash 变更集?

【问题讨论】:

    标签: git git-stash


    【解决方案1】:

    您可以使用“git show stash@{0}”(或任何存储编号;请参阅“git stash list”)获取存储的差异。提取单个文件的差异部分很容易。

    【讨论】:

    • 对于像我这样的人来说更简单:使用git show stash 显示最顶层的存储(通常是你唯一拥有的)。同样,您可以使用git diff head stash 显示当前分支和存储之间的差异。
    【解决方案2】:

    如果您使用git stash apply 而不是git stash pop,它会将存储应用到您的工作树,但仍会保留存储。

    完成此操作后,您可以add/commit 所需的文件,然后重置剩余的更改。

    【讨论】:

    • 要弹出特定的存储:git stash pop stash@{0}(列出隐藏的更改:git stash list
    • 如果存储中的其他文件导致合并冲突,git不会让我提交我想要的文件:(
    【解决方案3】:

    git stash 联机帮助页上,您可以阅读(在“讨论”部分,就在“选项”描述之后):

    一个 stash 表示为一个提交,其树记录了 工作目录,它的第一个父级是 HEAD 的提交,当 stash 已创建。

    因此您可以将存储(例如stash@{0} 是第一个/最顶层的存储)视为合并提交,并使用:

    $ git diff stash@{0}^1 stash@{0} -- <filename>
    

    解释:stash@{0}^1 表示给定存储的第一个父级,如上面的解释中所述,是隐藏更改的提交。我们使用这种形式的“git diff”(有两个提交),因为stash@{0} / refs/stash 是一个合并提交,我们必须告诉 git 我们想要比较哪个父级。更神秘:

    $ git diff stash@{0}^! -- <filename>
    

    也应该可以工作(请参阅 git rev-parse 手册页以了解“指定范围”部分中的 rev^! 语法解释)。

    同样,您可以使用git checkout 从存储中检查单个文件:

    $ git checkout stash@{0} -- <filename>
    

    或将其保存在另一个文件名下:

    $ git show stash@{0}:<full filename>  >  <newfile>
    

    $ git show stash@{0}:./<relative filename> > <newfile>
    

    注意这里 是相对于项目顶级目录的文件的完整路径名(想想:相对于stash@{0})。


    您可能需要保护stash@{0} 免受外壳扩展,即使用"stash@{0}"'stash@{0}'

    【讨论】:

    • 这太酷了...直到我阅读您的答案(这导致我添加了 git-checkout),我才真的了解 stash 是如何工作的。我真的不明白,当你进行存储时,git 会保存两个提交——一个用于索引的状态,一个用于工作副本的状态,它是索引和原始 HEAD 之间的合并。这解释了当我在存在存储时使用“gitk --all”可视化存储库时看到的奇怪树。
    • 大多数情况下,我发现 git checkout 应用程序是完成我想做的事情的最佳方式。但是我很好奇并仔细检查了git checkout 的手册页。它不能将文件放到另一个位置。对此有参考:stackoverflow.com/questions/888414/…
    • $ git checkout stash@{0} -- 非常有用,谢谢
    • 请注意,git checkout 方法会从存储中复制 exact 文件——它不会像 git stash apply 那样将其与工作目录中的内容合并。 (因此,如果您对创建存储的基础进行了任何更改,它们将会丢失)。
    • 请注意,为了让git stash apply 合并自文件被隐藏以来已在工作树中修改的文件中的更改,必须暂存工作树中的该文件。为了使自动合并起作用,不能在工作副本和隐藏的待合并副本中修改相同的文件。最后,stash apply 不会像 git stash pop 那样从 stash 中删除项目。
    【解决方案4】:

    要理解的最简单的概念(虽然可能不是最好的)是您更改了三个文件并且想要存储一个文件。

    如果您使用git stash 将它们全部隐藏起来,请git stash apply 将它们重新带回来,然后在相关文件上使用git checkout f.c 以有效地重置它。

    如果您想取消存储该文件,请运行 git reset --hard,然后再次运行 git stash apply,利用 git stash apply 不会从存储堆栈中清除差异这一事实。

    【讨论】:

      【解决方案5】:

      简答

      查看整个文件:git show stash@{0}:&lt;filename&gt;

      查看差异:git diff stash@{0}^1 stash@{0} -- &lt;filename&gt;

      【讨论】:

      • 我不认为他/她关心查看特定文件的存储空间——只弹出该文件。
      • 这就是我要找的。然后您可以将diff 替换为difftool 以使用您最喜欢的外部差异。
      • 我的 git repo 已损坏,但我已隐藏文件,但由于损坏而无法应用。我能够使用它并管道到一个新文件,对其进行比较,我很高兴。谢谢!
      【解决方案6】:

      编辑:参见cambunctious's answer,这基本上是我现在更喜欢的,因为它只使用存储中的更改,而不是将它们与您当前的状态进行比较。这使得操作具有附加性,自创建存储以来撤消已完成工作的机会要小得多。

      要以交互方式进行,您首先要这样做

      git diff stash^! -- path/to/relevant/file/in/stash.ext perhaps/another/file.ext > my.patch
      

      ...然后在文本编辑器中打开补丁文件,根据需要进行更改,然后执行

      git apply < my.patch
      

      cambunctious 的答案通过将一个命令直接传递给另一个命令来绕过交互性,如果您知道要从存储中进行所有更改,这很好。您可以将stash^! 编辑为具有所需累积更改的任何提交范围(但首先检查差异的输出)。

      如果应用补丁/差异失败,您可以将最后一个命令更改为git apply --reject,这将进行所有可以更改的更改,并将.rej 文件留在无法解决的冲突中。然后可以使用wiggle 应用.rej 文件,如下所示:

      wiggle --replace path/to/relevant/file/in/stash.ext{,.rej}
      

      这将解决冲突,或为您提供从合并中获得的冲突标记。

      如果您的发行版没有 wiggle,您可以直接构建它:

      cd /usr/local/src/
      git clone git://git.neil.brown.name/wiggle
      cd wiggle/
      git checkout v1.3
      make install
      

      以前的解决方案:有一种简单的方法可以从任何分支获取更改,包括存储:

      $ git checkout --patch stash@{0} path/to/file
      

      如果你想在很多部分打补丁,你可以省略文件规范。或者省略补丁(但不是路径)以获取对单个文件的所有更改。如果您有多个,请将 0 替换为 git stash list 中的存储编号。请注意,这类似于diff,并提供在分支之间应用所有差异。要从单个提交/存储中获取更改,请查看 git cherry-pick --no-commit

      【讨论】:

      • 这是从存储中复制确切的文件,还是合并?在复制的情况下,如果您在创建存储后有任何更改,它们将会丢失。
      • @Danijel 阅读git help checkout--patch 进行交互式合并,它应用您在 shell 中批准的任何块(或者如果您选择 edit 补丁,则应用您保存的任何块)。单独的路径将覆盖文件,就像我写的那样,“所有更改”。
      • 轻微改进:git config --global alias.applydiffat '!git checkout --patch "$1" -- $(git diff --name-only "$1"^ "$1")' -- 然后执行git applydiffat stash@{4} 仅使用在存储与其父级之间更改的文件。
      【解决方案7】:
      $ git checkout stash@{0} -- <filename>
      

      注意事项:

      1. 确保在“--”之后放置空格和文件名参数

      2. 将零 (0) 替换为您的特定存储编号。要获取存储列表,请使用:

        git stash list
        

      基于Jakub Narębski's answer -- 更短的版本

      【讨论】:

      • 您也可以使用存储名称git checkout stash^{/'Stash name here'} -- &lt;filename&gt;
      【解决方案8】:

      如果隐藏的文件需要与当前版本合并,请使用之前使用 diff 的方法。否则,您可以使用git pop 来取消存储它们,git add fileWantToKeep 用于暂存您的文件,并使用git stash save --keep-index 来存储除舞台上的内容之外的所有内容。 请记住,这种方式与以前的不同之处在于它从 stash 中“弹出”文件。以前的答案保留它git checkout stash@{0} -- &lt;filename&gt;,以便根据您的需要进行。

      【讨论】:

        【解决方案9】:

        使用以下命令将更改应用到存储中的文件到您的工作树。

        git diff stash^! -- <filename> | git apply
        

        这通常比使用git checkout 更好,因为您不会丢失自创建存储以来对文件所做的任何更改。

        【讨论】:

          猜你喜欢
          • 2011-07-16
          • 1970-01-01
          • 2015-04-29
          • 1970-01-01
          • 2016-04-09
          • 2012-11-30
          • 1970-01-01
          • 2014-11-06
          • 2015-04-07
          相关资源
          最近更新 更多