如何从git stash中提取单个文件(或对文件的更改)?

是否有可能在不弹出存储更改集的情况下从git存储中提取单个文件或文件的差异?

351278 次浏览

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

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

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

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

一个stash被表示为一个提交,其树记录了工作目录,其第一个父级是HEAD处的提交#36825;已创建。

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

$ 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解析手册页以了解rev^!语法的解释,在“指定范围”部分)。

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

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

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

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

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

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


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

要理解的最简单的概念(尽管可能不是最好的)是您更改了三个文件并且想要隐藏一个文件。

如果你做git stash来隐藏它们,git stash apply把它们带回来,然后git checkout f.c在有问题的文件上有效地重置它。

当您想取消存储该文件时,请执行git reset --hard,然后再次运行git stash apply,利用git stash apply没有清除存储堆栈中的差异这一事实。

简短的回答

查看整个文件:git show stash@{0}:<filename>

查看diff:git diff stash@{0}^1 stash@{0} -- <filename>

编辑:参见坎布奇蒂的回答,这基本上是我现在更喜欢的,因为它只使用存储中的更改,而不是将它们与您的当前状态进行比较。这使得操作成为添加剂,自存储创建以来撤消已完成工作的机会要小得多。

为了交互式地做,你首先要做

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

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

git apply < my.patch

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

如果应用补丁/diff失败,您可以将最后一个命令更改为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/wigglecd wiggle/git checkout v1.3make install

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

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

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

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

备注:

  1. 请确保您在 "--"和文件名参数之后放置空格

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

    git stash list

Based on Jakub Narębski's answer -- Shorter version

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

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

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

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