题 只存储使用Git更改过的多个文件中的一个文件?


如何在我的分支上只隐藏多个已更改文件中的一个?


2446
2018-06-14 20:52


起源


我不认为@ bukzor接受的答案是对问题的正确答案。 git stash --keep-index 确实保留了索引,但它仍然存在 一切  - 无论是在索引还是在。 - Raman
@Antonio在我看来,你的赏金实际上应该是一个单独的问题,因为原来的问题与TortoiseGit没有任何关系。 - JesusFreke
@JesusFreke是的,考虑到结果我可以节省50个代表:)这只是你被重定向到的问题,如果你试图搜索“部分藏匿tortoisegit”。 Tortoisegit似乎不是一个热门话题 stackoverflow.com/questions/tagged/tortoisegit - Antonio
>>>>>>>>> git diff -- *filename* > ~/patch 然后 git checkout -- *filename* 然后你可以重新应用补丁 git apply ~/patch - neaumusic
以下大多数现有答案都已过时。自Git 2.13(2017年第二季度)以来,它受到支持 git stash push [--] [<pathspec>...]。 - Ohad Schneider


答案:


警告

正如评论中所指出的,这会将所有内容都放入存储中,无论是分阶段还是非分阶段。 -step-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

1218
2017-11-30 21:28



我觉得这种方法要简单得多: stackoverflow.com/a/5506483/457268 - k0pernikus
我不确定为什么会被投票赞成。每个人都必须有不同的期望。原帖是在询问“我如何隐藏部分未提交的更改?”我用的时候 git stash save -k,是指数(绿色在 git stat)是保留,但是 整个 变更集(绿色和红色)进入藏匿处。这违反了OP的要求,“只保留一些变化”。我想隐藏一些红色(以备将来使用)。 - Pistos
如果你对@Pistos提出的问题的回答更感兴趣(就像我一样),那么看看这里: stackoverflow.com/questions/5506339/... - Raman
@Raman:太棒了! git stash -p 正是我想要的。我想知道这个开关是否最近才被添加。 - Pistos
警告: git stash --keep-index 被打破。如果您进行了更多更改,请尝试 git stash pop 稍后你会遇到合并冲突,因为藏匿包含你保存的已更改文件,而不仅仅是你没有保留的文件。例如:我更改文件A和B,然后存储B,因为我想测试A中的更改;我发现A的问题然后我修复了;我承诺A;现在我无法解除因为旧的A版本存在而没有充分的理由导致合并冲突。在实践中,A和B可能是许多文件,甚至可能是二进制图像或其他东西,所以我基本上不得不放弃并输掉B. - rjmunro


你也可以使用 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

2619
2017-07-31 11:59



这应该在顶部,因为它用最简单的方法直接回答问题。我希望我已经看过“a”/“d”选项(并在尝试之前完全阅读此答案),因为我有一个整个文件要添加到存储,另一个文件要完全排除。不过它对我来说很好;我没有太多的帅哥。 (也许2010年没有人建议原始问题发布时因为当时这个功能不在git中?) - Liam
不是。它是在事实发生后大约7年从Darcs借来的。 - nomen
我是一个TortoiseGit瘾君子。但是TortoiseGit不支持 stash -p。我认为这个答案是因为它仍然是最具互动性/用户友好性的。 - Antonio
你可能想要添加: git stash save -p my stash message;因为论证者的顺序不是很直观...... - Chris Maes
介于此之间 git log -p, 我觉得 -p 国旗必须意味着“做我想要的很酷的事情,但不知道如何表达。” - Kyle Strand


因为git基本上是关于管理所有存储库 内容 和索引(而不是一个或几个文件), git stash 交易,毫不奇怪, 与所有工作目录

实际上,自Git 2.13(2017年第2季度)以来,您可以存储单个文件,包括:

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

见“存储对特定文件的更改“更多。


原始答案(下面,2010年6月)是关于手动选择您想藏匿的内容。

Casebash 注释:

这个( stash --patch 原始解决方案)很好,但我经常修改了很多文件,因此使用补丁很烦人

bukzor回答 (upvoted,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内容

在相当繁琐的过程结束时,您将只有一个或几个文件被藏起来。


238
2018-06-14 21:23



这很好,但我经常修改了很多文件,因此使用补丁很烦人 - Casebash
@VonC:每个答案只有一个答案,这是一种很好的风格。此外,将他人的答案复制粘贴到您自己的答案是不礼貌的。 - bukzor
@bukzor:如果我编辑的答案看起来不合适,我很抱歉。我唯一的目的是让你的答案更具可见性。我已经再次编辑了我的帖子,以便让这个意图更清晰。 - VonC
@Kal:是的, stackoverflow.com/a/13941132/6309 建议一个 git reset (混合) - VonC
git is fundamentally about managing a all repository content and index and not one or several files  - 这个实施掩盖了正在解决的问题;这是一种解释,但不是理由。任何源控制系统都是关于“管理多个文件”。看看哪些评论得到最多投票。 - Victor Sergienko


什么时候 git stash -p (要么 git add -p 同 stash --keep-index)太麻烦了,我发现它更容易使用 diffcheckout 和 apply

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

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

然后是

git apply stashed.diff

76
2018-02-12 13:44



有趣的替代品 git add -p 我在上面的答案中提到过。 +1。 - VonC
请注意,如果您有二进制文件(如PNG),它们将不会输出到diff文件。所以这不是100%的解决方案。 - void.pointer
@RobertDailey:对我而言,这是一个有趣的观点 git diff > file.diff 和 git apply 是我通常的部分藏匿工具。我可能不得不考虑转换到 git stash -p 适用于较大的变更集。 - thekingoftruth
@thekingoftruth这是我用来创建补丁文件的别名,以及它 不 支持二进制文件: patch = log --pretty=email --patch-with-stat --reverse --full-index --binary。但请注意,这需要您对修补程序进行更改。 - void.pointer
如果要藏匿的文件是这样的话,这对我来说并不干净 ../../foo/bar.txt。补丁生成正常,但我需要移动到存储库根目录以获取要应用的补丁。因此,如果您遇到此问题 - 只需确保您从存储库根目录执行此操作。 - Michael Anderson


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


43
2017-10-31 07:10





使用 git stash push, 喜欢这个:

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

例如:

git stash push -- my/file.sh

这是自2017年春季发布的Git 2.13开始提供的。


34
2017-08-15 13:10



但我确实提到了 git stash push 已经在 我上面的回答 去年三月,5个月前。我在这里详细介绍了新的Git 2.13命令: stackoverflow.com/a/42963606/6309。 - VonC
我很高兴Git推进这么快,很长一段时间这是不可能的,然后2.13发布,突然有一个简单的解决方案可用! - sandstrom


另一种方法:

# 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作为“某处”(结果,最后还有一个额外的步骤:删除你放在藏匿处的cahnges,因为你也将它们移开了)。


25
2018-02-05 10:16



我最喜欢这种方法。它仅使用stash和revert命令在tortoisegit中提供简单的工作流程。 - Mark Ch
使用位置参考SO的答案是不可取的。位置随着评级的变化而变化。 - Bryan Ash
@BryanAsh嗯,这不重要。我正在讲一个轶事而不是真正提到其他答案。消息是我不喜欢社区喜欢的答案,而不是这些答案实际包含的答案。此外,第二个和第三个答案之间的900投票差距使得这在不久的将来不太可能改变,如果它应该改变,我总是可以编辑它以说“当时答案的顶部”。真的,我不知道在这种情况下这是怎么回事。 - Jasper


更新(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

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

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


22
2018-06-16 21:00



推荐 pushd 代替 cd 和 popd 在脚本结束时,如果脚本成功,则用户最终会在运行它之前的同一目录中。 - Nate
@Nate:据我所知,它只应该在用户获取脚本时更改用户的目录。如果你正常运行脚本(〜/ bin / git-stash-index),或者通过git(git stash-index),它会在一个单独的终端会话中运行,并且该会话中的任何工作目录更改都不会影响用户终端会话中的工作目录。如果不是这样,你是否知道一个常见的用例? (除了采购脚本,我不认为“常见”) - JesusFreke


由于在Git中创建分支是微不足道的,因此您可以创建一个临时分支并将单个文件检入其中。


16
2018-06-28 12:07



您无法使用非暂存编辑创建分支。你可以轻松移动 所有 编辑一个新的分支(stash / stash pop),但是你又回到原点:你如何只用其中一些编辑来测试你的分支,而不会丢失其他的? - bukzor
我刚创建了一个包含非分段编辑的分支。 - shangxiao
你不能 开关 分支,如果您有本地更改。但是,您可以创建一个新分支并有选择地添加/提交文件,然后创建另一个分支并以递归方式执行相同操作...然后签出原始分支并有选择地合并回来。我就是这么做的。它实际上似乎是做事的自然方式,因为你实际上是在创建功能分支。 - iain
@iain如果你有本地更改,只要不需要合并就可以切换分支。看到 示例要点。至少从Git v2.7.0开始就是如此。 - Colin D Bennett


以防你真正的意思 放弃更改 无论何时使用 git stash (并且不要使用git stash暂时存储它),在这种情况下你可以使用

git checkout -- <file>

[注意]

git stash 只是分支和做事的快速而简单的替代方案。


11
2017-12-19 11:40





将以下代码保存到文件中,例如,named stash。用法是 stash <filename_regex>。参数是文件完整路径的正则表达式。例如,要存储a / b / c.txt, stash a/b/c.txt 要么 stash .*/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
    }
  }
}

10
2017-07-11 18:55



好方法。我会选择这个作为答案。未来读者的提示:您必须在完整路径上匹配。例如stash subdir / foo.c - er0