当多个文件发生更改时,如何仅储存其中一个文件?

3726

我如何只储存分支上的一个已修改文件?


2
https://dev59.com/m3A75IYBdhLWcg3w6Nhj#19700341 比被接受的答案短得多,不需要任何额外的工具(比如像JesusFreke的脚本),并且它只会存储你想要存储的内容。 - frans
14
执行命令 git diff -- *filename* > ~/patch 将会生成一个补丁文件,然后通过命令 git checkout -- *filename* 可以撤销对该文件的改动。之后,你可以使用命令 git apply ~/patch 重新应用该补丁。请注意,这里的“filename”是指要操作的文件名。 - neaumusic
83
以下大多数回答已过时。自Git 2.13(2017年第2季度)起,它支持git stash push [--] [<pathspec>...]命令。请注意不改变原意,使语言更加通俗易懂。 - Ohad Schneider
32个回答

3549
git stash push -p -m "my commit message"

-p 选中应该被暂存的差异块; 整个文件也可以选择。

对于每个差异块,您将被提示进行几个操作:

   y - stash this hunk
   n - do not stash this hunk
   q - quit; do not stash this hunk or any of the remaining ones
   a - stash this hunk and all later hunks in the file
   d - do not stash this hunk or any of the later hunks in the file
   g - select a hunk to go to
   / - search for a hunk matching the given regex
   j - leave this hunk undecided, see next undecided hunk
   J - leave this hunk undecided, see next hunk
   k - leave this hunk undecided, see previous undecided hunk
   K - leave this hunk undecided, see previous hunk
   s - split the current hunk into smaller hunks
   e - manually edit the current hunk
   ? - print help

8
我是一个TortoiseGit的忠实用户。然而,TortoiseGit不支持"stash -p"功能。我给这个答案打分是因为它仍然是最互动/用户友好的。 - Antonio
35
您可能希望添加:git stash save -p 我的贮藏消息,因为参数的顺序不是很直观。 - Chris Maes
42
在这个语境下,“-p”应该表示“做我想要但不知道如何表达的酷炫操作”,与“git log -p”命令有关。 - Kyle Strand
7
这是一个正确的答案,但如果你要处理太多的代码块(“hunks”),它就变得无法使用了。 - Richard Smith
40
快速回答较新的问题:https://dev59.com/MG035IYBdhLWcg3wSOGr#5506483,提供给@sivck发表的回答。命令为`git stash push -m <stash_name> <file_path_to_stash>`。 - Deep
显示剩余11条评论

1403

免责声明:以下答案适用于git2.13之前的版本。若使用git2.13及以上版本,请查看下面的另一个答案


警告

如评论中所述,此命令将所有内容都放入stash中,包括已暂存和未暂存的更改。而--keep-index选项只是在stash操作后保留索引不变,这可能导致在稍后还原stash时出现合并冲突。


以下命令会把你尚未添加的所有更改都暂存起来。只需使用git add命令添加需要保存的更改,然后再运行该命令即可。

git stash --keep-index

例如,如果您想将旧提交分割成多个变更集,可以使用以下过程:

  1. git rebase -i <最后一个好的提交>
  2. 标记一些更改为编辑
  3. git reset HEAD^
  4. git add <要在此更改中保留的文件>
  5. git stash --keep-index
  6. 根据需要进行修复。不要忘记git add任何更改。
  7. git commit
  8. git stash pop
  9. 重复从#5开始的步骤,直至完成所有更改。
  10. git rebase --continue

48
我认为这种方法更加简单:https://dev59.com/MG035IYBdhLWcg3wSOGr#5506483 - k0pernikus
587
我不确定为什么这被点赞了。大家的期望一定跟我不同。原帖询问“如何只保存未提交更改的一部分?” 当我使用 git stash save -k 时,确实保留了索引(git stat 中的绿色),但是整个更改集(包括绿色和红色)都进入了stash。这违反了原帖的请求,“只保存一些更改”。我想只暂存一些红色部分(以备将来使用)。 - Pistos
71
如果你对@Pistos提出的问题更感兴趣(就像我一样),那么请看这里:https://dev59.com/MG035IYBdhLWcg3wSOGr。 - Raman
29
@Raman:太好了!git stash -p 正是我要找的。我想知道这个选项是不是最近才添加的。 - Pistos
14
警告:git stash --keep-index 命令存在问题。如果你在此之后继续进行更改,然后尝试 git stash pop,你会遇到合并冲突,因为存储的快照包括了你保留和修改的文件,而不仅仅是没有保留的文件。例如:我修改了文件 A 和 B,然后储藏了文件 B,因为我想测试 A 的更改;我发现 A 存在问题,于是修复了它;我提交了 A,但现在我无法取出储藏,因为旧版本的 A 没有任何好的理由在其中导致了合并冲突。实际上,A 和 B 可能是许多文件,甚至是二进制图像等,所以我基本上不得不放弃丢失 B。 - rjmunro
显示剩余10条评论

1012
自Git 2.13(2017年第二季度)起,您可以使用“git stash push”命令来储存单个文件。具体操作请参考git stash push
git stash push [-m <message>] [--] [<pathspec>...]

pathspec被提供给'git stash push'时,新的存储仅记录与路径模式匹配的文件的修改状态。更多信息请参见“将更改存储到特定文件中”。
简化示例:
 git stash push path/to/file

这个功能的测试用例展示了更多选项:

测试用例

test_expect_success 'stash with multiple pathspec arguments' '
    >foo &&
    >bar &&
    >extra &&
    git add foo bar extra &&

    git stash push -- foo bar &&   

    test_path_is_missing bar &&
    test_path_is_missing foo &&
    test_path_is_file extra &&

    git stash pop &&
    test_path_is_file foo &&
    test_path_is_file bar &&
    test_path_is_file extra

原始答案(见下文,2010年6月)是关于手动选择您想要存储的内容。

Casebash 评论说:

这很好(stash --patch的原始解决方案),但通常我已经修改了很多文件,所以使用打补丁很麻烦。

bukzor答案(2011年11月被赞同)提供了一个更实用的解决方案,基于
git add+git stash --keep-index
去看看并赞同他的答案,它应该是官方的答案(而不是我的)。

关于该选项,chhh在评论中指出了另一种替代工作流程:

在这样的藏匿之后,你应该"git reset --soft",以恢复清晰的暂存区:
为了回到原始状态——即清晰的暂存区并只有一些选择性的未暂存的修改,可以软重置索引以获得(没有像你——bukzor——这样提交任何东西)。


(原始答案2010年6月:手动隐藏)

然而,git stash save --patch可以让您实现部分隐藏您想要的内容:

使用 --patch,您可以交互式选择在HEAD和工作树之间的差异中的块进行隐藏。
藏匿条目被构造成其索引状态与您的仓库的索引状态相同,并且其工作树仅包含您交互选择的更改。 然后从您的工作树中回滚所选的更改。

然而,这将保存整个索引(可能不是您想要的,因为它可能包括已经索引的其他文件),以及部分工作树(看起来可能像您想要的工作树)。

git stash --patch --no-keep-index

如果--patch无法工作,手动处理可能会更好:
对于一个或多个文件,中间解决方案是:
  • 将它们复制到Git repo之外
    (实际上,eleotlecram提出了一个有趣的替代方案
  • git stash
  • 将它们复制回来
  • git stash # 这一次,只有你想要的文件被藏起来了
  • git stash pop stash@{1} # 重新应用所有文件的修改
  • git checkout -- afile # 将文件重置为HEAD内容,即本地修改之前的内容
在这个相当繁琐的过程结束时,你将只有一个或多个文件被藏起来。

4
这很好,但通常我修改了很多文件,所以使用补丁很麻烦。 - Casebash
1
@Kal:https://dev59.com/questions/MG035IYBdhLWcg3wSOGr#13941132 建议使用“git reset”(mixed)。 - VonC
5
Git 的本质是管理整个代码仓库的内容和索引,而不是单个或几个文件。这是实现方式超过解决问题本身的表现;这是一个解释,但不是一个正当理由。任何源代码控制系统都是关于“管理多个文件”的。只需看看哪些注释得到了最多的赞。 - Victor Sergienko
1
不建议使用 git stash --keep-index,正如在 bukzor 的回答评论中所指出的那样,它并不能做到你想要的效果。创建两个文件 foobar,将它们提交。对每个文件添加一行内容。执行 git add foo,然后执行 git stash --keep-index。期望的结果是你的 bar 文件的修改被暂存了起来,而你对 foo 文件的修改仍然存在且已经被暂存。但实际上,你对 foobar 两个文件的修改都被暂存了起来。如果你执行 git reset 并修改 foo 文件,由于冲突,你现在无法执行 git stash pop - Mark Amery
3
这也会隐藏所有已暂存的文件。所以请确保您没有暂存任何更改。 - Ville Miekk-oja
显示剩余4条评论

122

使用git stash push,像这样:

git stash push [--] [<pathspec>...]
例如:
git stash push -- my/file.sh

这个功能从Git 2.13开始可用,于2017年春季发布。


2
但是我在去年三月份的我的上面的答案中已经提到了git stash push。而且我在这里详细介绍了新的Git 2.13命令:https://dev59.com/hGct5IYBdhLWcg3wKqQd#42963606。 - VonC
7
@VonC,你是正确的,你也提到了正确的答案,然而,在这两个答案之间,这一个更容易阅读(没有混淆的文字,还有一个例子)。也许他们应该编辑你的答案。 - Utopik
3
那么,使用 git stash apply 来恢复被隐藏的更改,对吗? - Chad
4
有没有人能为我解释一下为什么一个如此不常见的词语“pathspec”被用来表示文件路径?或者它并不像我想象中那样不常见? - Nikhil Vandanapu
5
我认为术语“pathspec”之所以被使用,是因为它可以不仅仅表示简单的路径,还可以包括标准通配符、双星号通配符和更多奇特的语法。欲了解更多信息,请访问https://git-scm.com/docs/gitglossary,搜索“pathspec”。 - Simpleton
显示剩余2条评论

95

当使用git stash -p(或带有stash --keep-indexgit add -p)太麻烦时,我发现使用diffcheckoutapply更容易:

仅“隐藏”特定文件/目录:

git diff path/to/dir > stashed.diff
git checkout path/to/dir

之后

git apply stashed.diff

1
我在自己的回答中提到了一个有趣的替代方案,可以代替 git add -p。加一。 - VonC
14
请注意,如果您有二进制文件(如PNG),它们将不会输出到差异文件中。因此,这并不是一个100%的解决方案。 - void.pointer
1
@RobertDailey:对我来说,这是一个有趣的观点,因为git diff > file.diffgit apply是我通常使用的部分暂存工具。对于较大的更改集,我可能需要考虑切换到git stash -p - thekingoftruth
1
@thekingoftruth 这是我用于创建补丁文件的别名,并且它支持二进制文件patch = log --pretty=email --patch-with-stat --reverse --full-index --binary。请注意,这需要您提交补丁所做的更改。 - void.pointer
3
如果要储存的文件是 ../../foo/bar.txt 这样的路径,对我来说这种方法并不完美。虽然补丁可以生成,但我需要移动到存储库根目录才能应用补丁。因此,如果您在使用此方法时遇到问题,请确保您是从存储库根目录执行操作。 - Michael Anderson

59

如果您不想使用存储的更改指定消息,请在双破折号后传递文件名。

$ git stash -- filename.ext

如果是未跟踪/新文件,则需要首先将其暂存。

此方法适用于git版本2.13+。


2
那个答案太啰嗦了,这个简明扼要。如果它能帮助某人,我会留下它的。这个页面上没有人提到这个语法和结果 - 他们反而提到了 git stash push - sealocal
7
这是我在寻找的答案。谢谢! +1 - nicodp

58

假设你有3个文件

a.rb
b.rb
c.rb

如果您只想存储b.rb和c.rb而不是a.rb,可以像这样操作:

# commit the files temporarily you don't want to stash
git add a.rb
git commit -m "temp" 

# then stash the other files
git stash save "stash message"

# then undo the previous temp commit
git reset --soft HEAD^
git reset

完成了!希望对你有所帮助。


40

如果你只想要贮藏部分修改过的文件,只需将其他文件添加到暂存区,然后执行git stash push --keep-index

它将贮藏所有未在暂存区的修改过的文件。


这是不正确的。--keep-index 对于存储的内容没有影响。它只是将暂存的文件保持为暂存状态,并且存储会保留哪些文件被存储了的信息。所以如果你想要实际将这些信息还原到你的工作区,你需要使用 pop/apply 并加上 --index。否则,关于哪些是暂存的信息将被忽略,一切都会被还原为未暂存状态。 - undefined

30

另一种做法:

# Save everything
git stash 

# Re-apply everything, but keep the stash
git stash apply

git checkout <"files you don't want in your stash">

# Save only the things you wanted saved
git stash

# Re-apply the original state and drop it from your stash
git stash apply stash@{1}
git stash drop stash@{1}

git checkout <"files you put in your stash">

我在看到这个页面并不喜欢前两个答案(第一个答案没有回答问题,我也不太喜欢使用交互模式-p)后,想出了这个解决方案。
这个想法与@VonC建议的使用存储库外部的文件相同,您可以将想要的更改保存在某个地方,从中删除您不想要的更改,然后重新应用您移开的更改。但是,我使用git stash作为“某个地方”(因此,在最后多了一步:删除您放入stash中的更改,因为您将这些更改也移开了)。

1
我最喜欢这种方法。它在TortoiseGit中使用仅存储和还原命令提供了简单的工作流程。 - Mark Ch
1
参考SO上的答案使用位置不可取。位置会随着评分的变化而改变。 - Bryan Ash
2
@BryanAsh 嗯,这并不重要。我只是讲了一个轶事,而不是真正参考其他答案。我的意思是我不喜欢社区所喜欢的答案,而不是这些答案实际包含的内容。此外,第二和第三个答案之间的900票差距使得这种情况在不久的将来不太可能改变,如果它应该发生变化,我总是可以编辑它以说“当时的前两个答案”。实际上,在这种情况下,我不认为这会有任何问题。 - Jasper

30

你可以简单地这样做:

git stash push "filename"

或带有可选消息

git stash push -m "Some message" "filename"

2
这并没有添加任何新内容。Git stash push 已经在多个答案中提到了。 - JesusFreke
对我来说有效的是 git stash push -- <filepath>,这是最近GIT版本(v2.13>)中添加的方法。如果您运行 git status,可以获取 <filepath>。 - Samir K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接