3490

如何在我的分支上仅存储多个更改文件中的一个?

4

32 回答 32

3351
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
于 2013-07-31T11:59:56.827 回答
1398

免责声明:以下答案适用于 git 2.13 之前的 git。对于 git 2.13 及以上版本,请进一步查看另一个答案


警告

正如评论中所指出的,这会将所有内容都放入存储中,包括分阶段和非分阶段。--keep-index 只是在存储完成后单独保留索引。当您稍后弹出存储时,这可能会导致合并冲突。


这将隐藏您之前未添加的所有内容。只是git add你想保留的东西,然后运行它。

git stash --keep-index

例如,如果要将旧提交拆分为多个变更集,则可以使用以下过程:

  1. git rebase -i <last good commit>
  2. 将一些更改标记为edit.
  3. git reset HEAD^
  4. git add <files you want to keep in this change>
  5. git stash --keep-index
  6. 根据需要解决问题。不要忘记git add任何更改。
  7. git commit
  8. git stash pop
  9. 根据需要从 #5 开始重复。
  10. git rebase --continue
于 2011-11-30T21:28:16.753 回答
857

从 Git 2.13(2017 年第二季度)开始,您可以使用以下命令存储单个文件git stash push

git stash push [-m <message>] [--] [<pathspec>...]

pathspec被赋予 ' git stash push' 时,新的 stash 仅记录与 pathspec 匹配的文件的修改状态有关更多信息,请参阅“将更改存储到特定文件”。

简化示例:

 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 月)是关于手动选择要存储的内容。

凯斯巴什评论:

这(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 存储库之外
    (实际上,eleotlecram提出了一个有趣的替代方案
  • git stash
  • 把它们复制回来
  • git stash# 这次只存放你想要的文件
  • git stash pop stash@{1}# 重新应用所有文件修改
  • git checkout -- afile# 在任何本地修改之前将文件重置为 HEAD 内容

在这个相当繁琐的过程结束时,您将只存储一个或多个文件。

于 2010-06-14T21:23:56.130 回答
109

使用git stash push,像这样:

git stash push [--] [<pathspec>...]

例如:

git stash push -- my/file.sh

这是自 2017 年春季发布的 Git 2.13 以来可用的。

于 2017-08-15T13:10:16.090 回答
92

git stash -p(或git add -pstash --keep-index)太麻烦时,我发现它更易于使用diffcheckout并且apply

仅“存储”特定文件/目录:

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

然后之后

git apply stashed.diff
于 2014-02-12T13:44:40.300 回答
50

假设您有 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

你完成了!HTH。

于 2013-10-31T07:10:00.723 回答
39

如果您不想在隐藏的更改中指定消息,请在双破折号后传递文件名。

$ git stash -- filename.ext

如果它是一个未跟踪的/新文件,您必须先暂存它。

此方法适用于 git 版本 2.13+

于 2019-03-09T04:01:14.813 回答
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 的 cahnges,因为您也将它们移开了)。

于 2015-02-05T10:16:50.037 回答
27

你可以简单地这样做:

git stash push "filename"

或带有可选消息

git stash push -m "Some message" "filename"
于 2019-05-21T05:03:04.620 回答
27

如果您只想存储一些更改的文件,只需在Stage中添加其他文件,然后执行git stash push --keep-index

它将存储所有未暂存的更改文件

于 2019-12-12T07:08:45.120 回答
23

更新(2015 年 2 月 14 日)- 我稍微重写了脚本,以更好地处理冲突的情况,现在应该将其显示为未合并的冲突而不是 .rej 文件。


我经常发现与@bukzor 的方法相反更直观。也就是说,先进行一些更改,然后仅存储那些已分阶段的更改。

不幸的是,git 没有提供 git stash --only-index 或类似的功能,所以我编写了一个脚本来执行此操作。

#!/bin/sh

# first, go to the root of the git repo
cd `git rev-parse --show-toplevel`

# create a commit with only the stuff in staging
INDEXTREE=`git write-tree`
INDEXCOMMIT=`echo "" | git commit-tree $INDEXTREE -p HEAD`

# create a child commit with the changes in the working tree
git add -A
WORKINGTREE=`git write-tree`
WORKINGCOMMIT=`echo "" | git commit-tree $WORKINGTREE -p $INDEXCOMMIT`

# get back to a clean state with no changes, staged or otherwise
git reset -q --hard

# Cherry-pick the index changes back to the index, and stash.
# This cherry-pick is guaranteed to succeed
git cherry-pick -n $INDEXCOMMIT
git stash

# Now cherry-pick the working tree changes. This cherry-pick may fail
# due to conflicts
git cherry-pick -n $WORKINGCOMMIT

CONFLICTS=`git ls-files -u`
if test -z "$CONFLICTS"; then
    # If there are no conflicts, it's safe to reset, so that
    # any previously unstaged changes remain unstaged
    #
    # However, if there are conflicts, then we don't want to reset the files
    # and lose the merge/conflict info.
    git reset -q
fi

您可以将上述脚本保存在git-stash-index路径上的某个位置,然后可以将其作为 git stash-index 调用

# <hack hack hack>
git add <files that you want to stash>
git stash-index

现在 stash 包含一个新条目,其中仅包含您已暂存的更改,并且您的工作树仍包含任何未暂存的更改。

在某些情况下,工作树的更改可能依赖于索引的更改,因此当您存储索引更改时,工作树的更改会发生冲突。在这种情况下,您将获得可以使用 git merge/git mergetool/etc 解决的常见未合并冲突。

于 2013-06-16T21:00:00.447 回答
20

由于在 Git 中创建分支很简单,您可以创建一个临时分支并将各个文件签入其中。

于 2011-06-28T12:07:18.123 回答
13

以防万一您实际上是指在使用时丢弃更改git stash(并且不要真正使用 git stash 临时存储它),在这种情况下您可以使用

git checkout -- <file>

[注意]

git stash只是分支和做事的一种更快、更简单的替代方法。

于 2014-12-19T11:40:53.317 回答
12

将以下代码保存到文件中,例如,名为stash. 用法是stash <filename_regex>。参数是文件完整路径的正则表达式。例如,存储 a/b/c.txtstash a/b/c.txtstash .*/c.txt等。

$ chmod +x stash
$ stash .*.xml
$ stash xyz.xml

要复制到文件中的代码:

#! /usr/bin/expect --
log_user 0
set filename_regexp [lindex $argv 0]

spawn git stash -p

for {} 1 {} {
  expect {
    -re "diff --git a/($filename_regexp) " {
      set filename $expect_out(1,string)
    }
    "diff --git a/" {
      set filename ""
    }
    "Stash this hunk " {
      if {$filename == ""} {
        send "n\n"
      } else {
        send "a\n"
        send_user "$filename\n"
      }
    }
    "Stash deletion " {
      send "n\n"
    }
    eof {
      exit
    }
  }
}
于 2014-07-11T18:55:08.410 回答
9

VonC 将文件复制到 Git 存储库外部的“中间”解决方案的问题是您丢失了路径信息,这使得稍后将一堆文件复制回来有点麻烦。

发现使用 tar (类似的工具可能会做)而不是复制更容易:

  • tar cvf /tmp/stash.tar path/to/some/file path/to/some/other/file (... etc.)
  • git checkout path/to/some/file path/to/some/other/file
  • 混帐藏匿
  • tar xvf /tmp/stash.tar
  • 等等(参见 VonC 的“中级”建议)
于 2011-09-27T13:21:49.220 回答
8

有时我在提交之前对我的分支进行了不相关的更改,我想将其移动到另一个分支并单独提交(如 master)。我这样做:

git stash
git checkout master
git stash pop
git add <files that you want to commit>
git commit -m 'Minor feature'
git stash
git checkout topic1
git stash pop
...<resume work>...

请注意第一个stash&stash pop可以删除,您可以master在结帐时将所有更改转移到分支,但前提是没有冲突。此外,如果您为部分更改创建新分支,您将需要存储。

假设没有冲突并且没有新分支,您可以简化它:

git checkout master
git add <files that you want to commit>
git commit -m 'Minor feature'
git checkout topic1
...<resume work>...

甚至不需要藏匿...

于 2014-03-13T14:19:34.627 回答
8

这可以使用 SourceTree 分 3 步轻松完成。

  1. 暂时提交您不想隐藏的所有内容。
  2. Git 添加其他所有内容,然后将其隐藏。
  3. 通过运行 git reset 弹出你的临时提交,在你的临时提交之前定位提交。

这一切都可以在 SourceTree 中在几秒钟内完成,您只需单击要添加的文件(甚至单独的行)。添加后,只需将它们提交到临时提交。接下来,单击复选框以添加所有更改,然后单击 stash 以存储所有内容。随着隐藏的更改,浏览您的提交列表并在临时提交之前记下提交的哈希,然后运行“git reset hash_b4_temp_commit”,这基本上就像通过将分支重置为“弹出”提交在它之前提交。现在,你只剩下不想藏起来的东西了。

于 2016-01-11T19:41:10.673 回答
8

我会用git stash save --patch. 我不觉得交互性很烦人,因为在交互过程中有一些选项可以将所需的操作应用于整个文件。

于 2017-04-13T16:01:47.803 回答
7

这里的每一个答案都很复杂......

这要“藏起来”怎么办:

git diff /dir/to/file/file_to_stash > /tmp/stash.patch
git checkout -- /dir/to/file/file_to_stash

这将弹出文件更改:

git apply /tmp/stash.patch

与存储一个文件并将其重新弹出完全相同的行为。

于 2016-12-14T20:19:15.967 回答
4

我已经审查了这个问题的答案和评论以及许多类似的主题。请注意,为了能够存储任何特定的跟踪/未跟踪文件,以下命令均不正确:

  • git stash -p (--patch):手动选择帅哥,不包括未跟踪的文件
  • git stash -k (--keep-index):存储所有跟踪/未跟踪的文件并将它们保存在工作目录中
  • git stash -u (--include-untracked):存储所有跟踪/未跟踪的文件
  • git stash -p (--patch) -u (--include-untracked): 无效命令

目前,能够存储任何特定跟踪/未跟踪文件的最合理方法是:

  • 临时提交您不想存储的文件
  • 添加和存储
  • 弹出临时提交

我在回答另一个问题时为此过程编写了一个简单的脚本,这里有在 SourceTree 中执行该过程的步骤

于 2017-03-22T04:36:10.183 回答
4

当您尝试在两个分支之间切换时,就会出现这种情况。

尝试使用“ git add filepath”添加文件。

稍后执行此行

git stash --keep-index

于 2015-07-21T19:26:58.240 回答
4

解决方案

局部变化:

  • file_A(已修改)未暂存
  • file_B(已修改)未暂存
  • file_C(已修改)未暂存

要创建一个仅对 file_C 进行更改的存储“my_stash

1. git add file_C
2. git stash save --keep-index temp_stash
3. git stash save my_stash
4. git stash pop stash@#{1}

完毕。


解释

  1. file_C添加到暂存区
  2. 创建一个名为“temp_stash”的临时存储并将更改保留在 file_C
  3. 仅使用 file_C 上的更改创建想要的存储(“my_stash”)
  4. 在本地代码上应用“temp_stash”(file_A 和 file_B)中的更改并删除存储

您可以在步骤之间使用git status来查看发生了什么。

于 2017-03-29T15:04:36.407 回答
3

对于 VS Code 的用户。Git 侧边栏视图中更改组的存储按钮将仅存储组中的文件。因此,如果您将一些文件移出该组,则可以存储剩余的文件。我知道在不恢复更改的情况下将一些文件移出那里的唯一方法是暂存它们。所以:

  1. 暂存您不想存放的文件
  2. 单击更改组标题中的存储按钮
  3. 取消暂存您移开的文件
于 2021-05-27T14:24:20.203 回答
3

要存储单个文件,请使用git stash --patch [file].

这将提示:Stash this hunk [y,n,q,a,d,j,J,g,/,e,?]? ?。只需键入a(将这个大块和所有后来的大块存储在文件中)就可以了。

于 2018-09-14T19:22:34.987 回答
2

类似的情况。确实提交并意识到这不好。

git commit -a -m "message"
git log -p

根据答案,这对我有所帮助。

# revert to previous state, keeping the files changed
git reset HEAD~
#make sure it's ok
git diff
git status
#revert the file we don't want to be within the commit
git checkout specs/nagios/nagios.spec
#make sure it's ok
git status
git diff
#now go ahead with commit
git commit -a -m "same|new message"
#eventually push tu remote
git push
于 2012-06-20T06:49:47.320 回答
2

我不知道如何在命令行上执行此操作,仅使用 SourceTree。假设您更改了文件 A,并在文件 B 中有两个更改块。如果您只想将第二个块存储在文件 B 中,而其他所有内容都保持不变,请执行以下操作:

  1. 舞台一切
  2. 对您的工作副本执行更改以撤消文件 A 中的所有更改。(例如,启动外部差异工具并使文件匹配。)
  3. 使文件 B 看起来好像只应用了第二次更改。(例如启动外部差异工具并撤消第一次更改。)
  4. 使用“保持分阶段更改”创建一个存储。
  5. 取消所有内容
  6. 完毕!
于 2015-04-30T11:15:52.270 回答
2

在这种情况下,我git add -p(交互式),git commit -m blah然后在必要时存放剩下的东西。

于 2013-04-18T21:10:07.213 回答
2
git add .                           //stage all the files
git reset <pathToFileWillBeStashed> //unstage file which will be stashed
git stash                           //stash the file(s)
git reset .                         // unstage all staged files
git stash pop                       // unstash file(s)
于 2018-07-20T10:46:51.517 回答
0

我没有找到我需要的答案,这很简单:

git add -A
git reset HEAD fileThatYouWantToStash
git commit -m "committing all but one file"
git stash

这仅存储一个文件。

于 2017-02-27T08:39:24.040 回答
0

我也想做同样的事情,但当我再想一想时,我意识到我真的不想为了保留一个文件而费尽心思。将它复制到其他地方并添加一些评论以提醒我为什么将其保留在那里会容易得多。

不要误会我的意思:有很多原因可以让您只存储一个文件,一般来说,使用版本控制软件始终是最佳实践。但是,请确保您没有浪费时间。就我而言,我只想保留一个文件并丢弃所有其他更改,并在切换到新分支后将其弹出。所以cp/mv工作得很好。

于 2020-04-23T16:11:23.300 回答
0

一种复杂的方法是首先提交所有内容:

git add -u
git commit // creates commit with sha-1 A

重置回原始提交,但从新提交中签出 the_one_file:

git reset --hard HEAD^
git checkout A path/to/the_one_file

现在您可以存储 the_one_file:

git stash

通过将提交的内容保存在文件系统中来进行清理,同时重置为原始提交:

git reset --hard A
git reset --soft HEAD^

是的,有点尴尬……

于 2017-02-08T18:17:21.933 回答
-1

您还可以选择仅存储单个文件、文件集合或文件中的单个更改。如果您将 -p 选项(或 --patch)传递给 git stash,它将遍历工作副本中每个更改的“hunk”并询问您是否希望将其存储:

$ git stash -p

按如下键之一,它将运行该命令

Command   Description 
y         stash this hunk  
/         search for a hunk by regex .  
?         help .   
n         don't stash this hunk .    
q         quit (any hunks that have already been selected will be stashed) .   
s         split this hunk into smaller hunks    
于 2020-02-25T09:47:47.627 回答