在git 2.13之前,只保存多个已更改的文件中的一个文件

时间:2010-06-14 20:52:34

标签: git git-stash

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

33 个答案:

答案 0 :(得分:2893)

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

答案 1 :(得分:1288)

更新以下答案适用于git 2.13之前的git。对于git 2.13及更高版本,请查看How can I git stash a specific file?

警告

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

答案 2 :(得分:335)

由于git基本上是关于管理所有存储库内容和索引(而不是一个或多个文件),git stash毫不奇怪地将与所有工作目录进行交易< / DEL>

实际上,自Git 2.13(2017年第2季度)以来,您可以使用git stash push隐藏单个文件:

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

pathspec被赋予“git stash push”时,新存储仅记录与pathspec匹配的文件的修改状态

有关详情,请参阅“Stash changes to specific files”。

The test case不言自明:

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原始解决方案)很不错,但我经常修改了很多文件,因此使用补丁很烦人

bukzoranswer(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建议interesting alternative
  • git stash
  • 将它们复制回来
  • git stash#这次,只有您想要的文件被隐藏
  • git stash pop stash@{1}#重新应用所有文件修改
  • git checkout -- afile#在任何本地修改之前将文件重置为HEAD内容

在这个相当繁琐的过程结束时,你只会藏匿一个或几个文件。

答案 3 :(得分:81)

git stash -p(或git add -pstash --keep-index)过于繁琐时,我发现使用diffcheckout和{{1}更容易}:

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

apply

之后

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

答案 4 :(得分:66)

使用git stash push,如下所示:

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

例如:

git stash push -- my/file.sh

自2017年春季发布的Git 2.13开始提供此功能。

答案 5 :(得分:45)

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

答案 6 :(得分:27)

另一种方法:

# 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作为&#34;某处&#34; (结果,最后还有一个额外的步骤:移除你藏在藏匿处的那些东西,因为你也把它们移开了。)

答案 7 :(得分: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

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

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

答案 8 :(得分:19)

由于在Git中创建分支是微不足道的,你可以创建一个临时分支并检查单个文件。

答案 9 :(得分:11)

将以下代码保存到文件中,例如,名为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 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 :(得分:11)

万一你实际上意味着丢弃更改,只要你使用git stash(并且真的不使用git stash暂时存储它),在这种情况下你可以使用

git checkout -- <file>

[注意]

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

答案 11 :(得分:7)

使用SourceTree可以在3个步骤中轻松完成。

  1. 暂时承诺你不想要的东西。
  2. Git添加其他所有内容,然后存储它。
  3. 通过运行git reset来弹出临时提交,在临时提交之前定位提交。
  4. 这可以在SourceTree中在几秒钟内完成,您可以在其中单击要添加的文件(甚至单独的行)。添加后,只需将它们提交给临时提交。接下来,单击复选框以添加所有更改,然后单击存储以存储所有内容。通过隐藏的更改,浏览提交列表并在临时提交之前记下提交的哈希,然后运行&#39; git reset hash_b4_temp_commit&#39;,这基本上就像&#34;弹出&# 34;通过将分支重置为它之前的提交来提交。现在,你只剩下你不想藏匿的东西。

答案 12 :(得分:7)

我会使用git stash save --patch。我没有发现交互性很烦人,因为在它期间有选项将所需的操作应用于整个文件。

答案 13 :(得分:7)

有时候,在我提交之前,我已经在我的分支上做了一个无关的更改,我想将它移动到另一个分支并单独提交(如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&amp; stash pop可以被删除,您可以在结帐时将所有更改带到master分支,但前提是没有冲突。此外,如果您要为部分更改创建新分支,则需要存储。

您可以在没有冲突且没有新分支的情况下简化它:

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

甚至不需要藏匿......

答案 14 :(得分:7)

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
  • git stash
  • tar xvf /tmp/stash.tar
  • 等。 (见VonC的“中间”建议)

答案 15 :(得分:6)

这里的每个答案都是如此复杂......

这个“藏匿”怎么样:

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

这会弹出文件更改:

git apply /tmp/stash.patch

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

答案 16 :(得分:3)

我已经回顾了这个和许多类似线程的答案和评论。请注意,以下任何命令都不能用于存储任何特定跟踪/未跟踪文件的目的

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

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

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

I wrote a simple script for this procedure in an answer to another question,还有steps for performing the procedure in SourceTree here

答案 17 :(得分:3)

解决方案

本地更改:

  • 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
  4. 上的更改创建想要的存储(“my_stash”)
  5. 将“temp_stash”(file_A和file_B)中的更改应用于您的本地代码并删除存储
  6. 您可以在步骤之间使用 git status 来查看正在发生的事情。

答案 18 :(得分:2)

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

尝试使用&#34; git add filepath&#34;。

添加文件

稍后执行此行

  

git stash --keep-index

答案 19 :(得分:2)

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

答案 20 :(得分:2)

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

$ git stash -- filename.ext

如果它是未跟踪/新的文件,则必须先将其登台。

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

答案 21 :(得分: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)

答案 22 :(得分:1)

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

  1. 暂存您不想隐藏的文件
  2. 点击更改组标题中的隐藏按钮
  3. 取消暂存您移开的文件

答案 23 :(得分:1)

对于 SourceTree 用户,选择要存储的未提交文件,然后单击顶部的工具栏按钮:

enter image description here

现在存储区将在侧栏中列出;您可以通过右键单击将其应用于新签出的分支。

答案 24 :(得分:1)

类似的情况。提交并意识到它不行。

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

答案 25 :(得分:1)

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

  1. 舞台上的一切
  2. 对工作副本执行更改,撤消文件A中的所有更改。(例如,启动外部差异工具并使文件匹配。)
  3. 使文件B看起来好像只对其应用了第二次更改。 (例如,启动外部差异工具并撤消第一次更改。)
  4. 使用&#34;保持分阶段更改&#34;。
  5. 创建存储
  6. 取消所有内容
  7. 完成!

答案 26 :(得分:0)

快速解答


要在git中还原特定的更改文件,可以执行以下行:

git checkout <branch-name> -- <file-path>

这是一个实际示例:

git checkout master -- battery_monitoring/msg_passing.py

答案 27 :(得分:0)

我发现没有答案可以满足我的需要,而且这很简单:

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

这只会存放一个文件。

答案 28 :(得分:0)

如果您想隐藏一些更改的文件,只需

  

阶段中添加您不想隐藏的文件,然后执行git stash save --keep-index

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

答案 29 :(得分:0)

我也想做同样的事情,但是当我再次考虑它时,我意识到我真的不想只为了保留一个文件而烦恼。对我来说,将其复制到其他位置并添加一些注释来提醒我为什么将其保留在这里会容易得多。

不要误会我的意思:为什么要隐藏一个文件有很多原因,通常,使用版本控制软件始终是最佳实践。但是,请确保您没有浪费时间。就我而言,我只想保留一个文件并放弃所有其他更改,然后在切换到新分支后将其弹出。因此cp/mv运作良好。

答案 30 :(得分: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^

是的,有点尴尬......

答案 31 :(得分:-1)

您可以简单地做到这一点:

git stash push "filename"

或带有可选消息

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

答案 32 :(得分:-1)

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

$ 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