在使用 Git 进行更改的多个文件中,只能存储一个文件?

如何仅将分支中多个已更改文件之一存储?

答案

您还可以使用git stash save -p "my commit message" 。这样,您可以选择应将哪些块添加到存储中,也可以选择整个文件。

对于每个大块,系统将提示您一些操作:

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

更新 :以下答案适用于 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

由于 git 从根本上讲是管理所有存储库的内容和索引(而不是一个或多个文件),因此git stash交易并不奇怪, 与所有工作目录

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

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

当给 ' git stash push ' 提供pathspec ,新的 stash 仅记录与 pathspec 匹配的文件的修改状态

有关更多信息,请参见 “将更改存储到特定文件 ”。

测试用例是不言自明的:

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原始解决方案)很好,但是我经常修改了很多文件,所以使用 patch 很烦人

bukzor答案 (于 2011 年 11 月更新)提出了一个更实际的解决方案,该解决方案基于
git add + git stash --keep-index
去看看并支持他的答案,应该是官方的答案(而不是我的答案)。

关于该选项, chhh在注释中指出了另一种工作流程:

您应该在这样的git reset --soft之后执行 “ git reset --soft ”,以使您清楚地恢复工作:
为了达到原始状态 - 这是一个清晰的登台区域,并且只选择了一些未分级的修改,就可以轻柔地重置索引以获取(无需像 bukzor 那样执行任何操作)。


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

但是, git stash save --patch可以使您实现部分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 内容

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

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

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

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

然后呢

git apply stashed.diff

使用git stash push ,如下所示:

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

例如:

git stash push -- my/file.sh

自 2017 年春季发布的 Git 2.13 开始可用。

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

另一种方法是:

# 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,因为您也将它们移开了)。

更新(2/14/2015)- 我稍微重写了一下脚本,以更好地处理冲突的情况,现在应将其显示为未合并的冲突,而不是. 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

现在,存储区包含一个新条目,其中仅包含您已暂存的更改,而工作树仍包含所有未暂存的更改。

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

由于在 Git 中创建分支很简单,因此您只需创建一个临时分支并将单个文件签入其中即可。

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

$ git stash -- filename.ext

如果是未跟踪 / 新文件,则必须先上载它。

此方法在 git 版本 2.13 + 中有效