如何从git存放区中提取单个文件(或更改文件)?


772

我想知道是否有可能从git存储中提取单个文件或文件的差异而不弹出存储变更集。

有人可以为此提供一些建议/想法吗?

Answers:


1130

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

存储区表示为提交,其树记录了工作目录的状态,其第一个父级是创建存储区时在HEAD的提交。

因此,您可以将存储(例如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>

请注意,这里<full filename>是相对于项目顶层目录的文件的完整路径名(认为:相对于stash@{0}))。


您可能需要防止stash@{0}shell扩展,即使用"stash@{0}"'stash@{0}'


8
真是太酷了…… 在阅读您的答案之前,我还不真正了解存储的工作原理(这使我想到了git-checkout的补充)。我真的不明白,当您执行存储操作时,git保存了两次提交-一个用于索引状态,一个用于工作副本状态,即索引和原始HEAD之间的合并。这解释了当存在存储时,当我使用“ gitk --all”可视化存储库时看到的奇怪树。
帕特诺兹,2009年

4
通常,我发现git checkout应用程序是完成我想要做的最好的方法。但是我很好奇并仔细检查了git checkout手册页。它不能将文件拖放到另一个位置。在下面有对此的引用:stackoverflow.com/questions/888414/…–
Danny

84
$ git checkout stash @ {0}-<filename>非常有用,谢谢
万有引力

43
请注意,该git checkout方法从存储中复制了确切的文件-不会像git stash apply将文件与工作目录中的文件合并一样。(因此,如果您在创建存储的基础上进行了任何更改,则这些更改将丢失)。
peterflynn

4
请注意,为了git stash apply合并自文件保存以来在工作树中已修改的文件中的更改,必须暂存工作树中的该文件。为了使自动合并生效,不能在工作副本和要合并的隐藏副本中修改相同的文件。最后,隐藏存储不会像从存储库中删除项目一样git stash pop
Ville

46

如果您使用git stash apply而不是git stash pop,它将把隐藏项应用到您的工作树上,但仍保留该隐藏项。

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


2
要弹出特定的藏匿处:git stash pop stash@{0}:(列表中藏匿的变化git stash list
MrYoshiji

如果存储中的其他文件引起合并冲突,则git不允许我对所需的文件进行提交:(
令人生畏的

33

有一个简单的方法可以从任何分支(包括存储)获取更改:

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

如果要在许多部分进行修补,则可以省略文件规范。或省略补丁(但不删除路径)以将所有更改更改为单个文件。如果您有多个,请0用中的存储号替换git stash list。请注意,这就像diff,并提供了应用分支之间所有差异的功能。要仅通过一次提交/存储获得更改,请查看git cherry-pick --no-commit


这会从存储中复制确切的文件,还是合并?如果是复制,则自创建存储以来,如果您有任何更改,它们将丢失。
Danijel

2
@Danijel阅读git help checkout--patch进行交互式合并,它将应用您在shell中批准的任何块(如果选择e删除补丁,则保存任何块)。就像我写的那样,仅路径会覆盖文件“所有更改”。
沃尔夫

1
稍有改进: git config --global alias.applydiffat '!git checkout --patch "$1" -- $(git diff --name-only "$1"^ "$1")' -然后git applydiffat stash@{4}只使用在存储库及其父目录之间更改的文件。
标记

25

简短答案

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

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


1
我认为他/他不关心查看特定文件的存储-只弹出该文件。
Amalgovinus

1
这就是我想要的。然后,您可以替换diffdifftool以使用您喜欢的外部差异。
Peet Brits

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

笔记:

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

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

    git stash list
    

基于JakubNarębski的答案 -较短的版本


11

您可以使用“ git show stash@{0}”(或其他存储数量,请参见“ git存储列表”)获取存储的差异。为单个文件提取diff的部分很容易。


2
对于像我这样的人来说,仍然更简单:用于git show stash显示最高的存储(通常是您唯一的存储)。同样,您可以使用来显示当前分支和存储之间的差异git diff head stash
Dizzley

5

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

如果您确实git stash将它们全部藏起来,请git stash apply再次将它们放回原处,然后再将它们git checkout f.c放在有问题的文件上以有效地将其重置。

如果要取消隐藏该文件,请运行a git reset --hard,然后git stash apply再次运行,以利用git stash apply不会从隐藏堆栈中清除差异的事实。


1

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


0

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

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

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.