如何在已更改的多个文件中仅隐藏一个文件?

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

1127409 次浏览

从Git 2.13(Q2 2017)开始,您可以隐藏单个文件,#0

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月)提出了一个更实用的解决方案,基于
#0+#1.
去看看并投票支持他的答案,这应该是官方的(而不是我的)。

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

你应该在这样的藏匿之后“git reset --soft”才能让你的清晰分期回来:
为了到达原始状态-这是一个明确的暂存区域,并且只有一些选择的未暂存修改,可以轻轻地重置索引以获取(而不会像你-bukzor-一样提交任何东西)。


(原答复2010年6月:手动藏匿)

然而,git stash save --patch可以让你实现你所追求的部分存储:

使用--patch,您可以交互地从HEAD和要存储的工作树之间的差异中选择大块。
存储项的构造方式使其索引状态与存储库的索引状态相同,并且其工作树仅包含您以交互方式选择的更改。然后从您的工作树回滚选定的更改。

但是,这将保存完整的索引(可能不是您想要的,因为它可能包括已索引的其他文件)和部分工作树(可能看起来像您想要存储的文件)。

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

可能更合适。


如果--patch不起作用,则手动过程可能:

对于一个或多个文件,中间解决方案是:

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

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

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

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

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

  • tar cvf /tmp/stash.tar路径/到/一些/文件路径/到/一些/其他/文件(…等)
  • git签出路径/到/一些/文件路径/到/一些/其他/文件
  • git stash
  • 焦油xvf /tmp/stash.tar
  • 等等(见VonC的“中间”建议)

免责声明:以下答案适用于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 commit -a -m "message"git log -p

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

# revert to previous state, keeping the files changedgit reset HEAD~#make sure it's okgit diffgit status#revert the file we don't want to be within the commitgit checkout specs/nagios/nagios.spec#make sure it's okgit statusgit diff#now go ahead with commitgit commit -a -m "same|new message"#eventually push tu remotegit push

在这种情况下,我git add -p(交互式),git commit -m blah,然后在必要时隐藏剩余的内容。

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


我经常发现与@bukzor的方法相反更直观。也就是说,暂存一些更改,然后只存储那些暂存的更改。

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

#!/bin/sh
# first, go to the root of the git repocd `git rev-parse --show-toplevel`
# create a commit with only the stuff in stagingINDEXTREE=`git write-tree`INDEXCOMMIT=`echo "" | git commit-tree $INDEXTREE -p HEAD`
# create a child commit with the changes in the working treegit add -AWORKINGTREE=`git write-tree`WORKINGCOMMIT=`echo "" | git commit-tree $WORKINGTREE -p $INDEXCOMMIT`
# get back to a clean state with no changes, staged or otherwisegit reset -q --hard
# Cherry-pick the index changes back to the index, and stash.# This cherry-pick is guaranteed to succeedgit cherry-pick -n $INDEXCOMMITgit stash
# Now cherry-pick the working tree changes. This cherry-pick may fail# due to conflictsgit 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 -qfi

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

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

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

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

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

-p让您选择应该隐藏的大块;也可以选择整个文件。

系统会提示您为每个大块执行一些操作:

   y - stash this hunkn - do not stash this hunkq - quit; do not stash this hunk or any of the remaining onesa - stash this hunk and all later hunks in the filed - do not stash this hunk or any of the later hunks in the fileg - select a hunk to go to/ - search for a hunk matching the given regexj - leave this hunk undecided, see next undecided hunkJ - leave this hunk undecided, see next hunkk - leave this hunk undecided, see previous undecided hunkK - leave this hunk undecided, see previous hunks - split the current hunk into smaller hunkse - manually edit the current hunk? - print help

假设你有3个文件

a.rbb.rbc.rb

你只想藏b.rbc.rb而不是a.rb

你可以做这样的事

# commit the files temporarily you don't want to stashgit add a.rbgit commit -m "temp"
# then stash the other filesgit stash save "stash message"
# then undo the previous temp commitgit reset --soft HEAD^git reset

你完蛋了!HTH。

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

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

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

那之后呢

git apply stashed.diff

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

git stashgit checkout mastergit stash popgit add <files that you want to commit>git commit -m 'Minor feature'git stashgit checkout topic1git stash pop...<resume work>...

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

你可以简化它,假设没有冲突,也没有新的分支:

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

Stash甚至不需要…

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

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

要复制到文件中的代码:

#! /usr/bin/expect --log_user 0set 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}}}

如果你在使用git stash时实际上是指丢弃更改(并且没有真正使用git stash来临时存储它),在这种情况下,你可以使用

git checkout -- <file>

[]

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

另一种方法来做到这一点:

# Save everythinggit stash
# Re-apply everything, but keep the stashgit stash apply
git checkout <"files you don't want in your stash">
# Save only the things you wanted savedgit stash
# Re-apply the original state and drop it from your stashgit stash apply stash@{1}git stash drop stash@{1}
git checkout <"files you put in your stash">

在我(再次)来到这个页面并且不喜欢前两个答案之后,我想到了这个问题(第一个答案只是没有回答问题,我不太喜欢使用-p交互模式)。

这个想法与@VonC建议使用存储库之外的文件相同,您将所需的更改保存在某处,删除存储中不想要的更改,然后重新应用您移动的更改。然而,我使用git stash作为“某处”(因此,最后还有一个额外的步骤:删除您放在存储库中的文件,因为您也移动了这些文件)。

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

  1. 安排好一切
  2. 对您的工作副本执行更改,撤消文件A中的所有更改(例如启动外部diff工具并使文件匹配。)
  3. 使文件B看起来好像只对其应用了第二次更改。(例如启动外部diff工具并撤消第一次更改。)
  4. 使用“保持分阶段更改”创建存储。
  5. 放下一切
  6. 成交!

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

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

稍后执行此行

git stash --keep-index

这可以通过使用SourceTree的3个步骤轻松完成。

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

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

这里的每个答案都很复杂…

关于这个“藏匿”:

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

这将弹出文件更改:

git apply /tmp/stash.patch

与隐藏一个文件并将其弹出完全相同的行为。

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

git add -ugit 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 Agit reset --soft HEAD^

是啊有点尴尬…

我没有找到我需要的答案,这很容易:

git add -Agit reset HEAD fileThatYouWantToStashgit commit -m "committing all but one file"git stash

这正好隐藏了一个文件。

我已经查看了这个和许多类似线程的答案和评论。请注意,以下命令中没有一个是正确的,以便能够隐藏任何特定的跟踪/未跟踪文件

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

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

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

我为这个过程写了一个简单的脚本来回答另一个问题。,还有在此处的SourceTree中执行过程的步骤

解决方案

局部变化:

  • file_A(修改)未上演
  • file_B(修改)未上演
  • file_C(修改)未上演

要创建仅对file_C进行更改的存储“my_stash”:

1. git add file_C2. git stash save --keep-index temp_stash3. git stash save my_stash4. git stash pop stash@#{1}

成交


补充说明

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

您可以在步骤之间使用git状态来查看正在发生的事情。

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

使用git stash push,像这样:

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

例如:

git stash push -- my/file.sh

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

git add .                           //stage all the filesgit reset <pathToFileWillBeStashed> //unstage file which will be stashedgit stash                           //stash the file(s)git reset .                         // unstage all staged filesgit stash pop                       // unstash file(s)

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

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

如果您不想使用隐藏的更改指定消息,请在双划线后传递文件名。

$ git stash -- filename.ext

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

此方法适用于git版本2.13+

你可以简单地这样做:

git stash push "filename"

或带有可选消息

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

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

它将隐藏所有未上演更改的文件

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

$ git stash -p

按下其中一个键,它将运行该命令

Command   Descriptiony         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

我也想做同样的事情,但是当我再次考虑这个问题时,我意识到我真的不想为了保留一个文件而经历所有的麻烦。对我来说,把它复制到其他地方并添加一些注释来提醒我为什么要把它保存在那里要容易得多。

别误会我的意思:你有很多原因想隐藏一个文件,一般来说,使用版本控制软件总是最好的做法。但是,确保你没有浪费时间。在我的情况下,我只是想保留一个文件并丢弃所有其他更改,并在我切换到新分支后弹出它。所以cp/mv工作得很好。

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

  1. 暂存您不想隐藏的文件
  2. 单击变化组标题中的存储按钮
  3. 取消你移开的文件