仅存储使用Git更改的多个文件中的一个文件?

Rac*_*hel 2895 git git-stash

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

kon*_*ski 2949

你也可以使用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
Run Code Online (Sandbox Code Playgroud)

  • 你可能想添加:`git stash save -p my stash message`; 因为论证者的顺序不是很直观...... (27认同)
  • 在较新问题上发布的快速回答:/sf/answers/385453841/ by @svick `git stash push -m <stash_name> <file_path_to_stash>` (18认同)
  • 在这个和[`git log -p`]之间(/sf/answers/1568857671/),我认为`-p`标志必须意味着"做我想要的很酷的东西,但不知道如何表达." (12认同)
  • 这是一个正确的答案,但如果你有太多的帅哥需要解决,它就会变得无用。 (7认同)
  • 我是一个TortoiseGit瘾君子.但是TortoiseGit不支持`stash -p`.我给出了这个答案,因为它仍然是最具互动性/用户友好性的. (6认同)
  • 它不是.事实发生后约7年,它是从Darcs借来的. (5认同)
  • @MaxCascone奇怪,我两个都紧挨着。也许 GitLens 扩展添加了这个选项? (2认同)

buk*_*zor 1322

警告

正如评论中所指出的,这会将所有内容都放入存储中,无论是分阶段还是非分阶段.在保存完成后,--keep-index只保留索引.以后弹出存储时,这可能会导致合并冲突.


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

git stash --keep-index
Run Code Online (Sandbox Code Playgroud)

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

  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 stash save -k`时,是的,索引(`git stat`中的绿色)被保留,但*whole*changeset(绿色和红色)进入存储.这违反了OP的要求,"只保留一些变化".我想隐藏一些红色(以备将来使用). (544认同)
  • 如果你对@Pistos提出的问题的回答更感兴趣(就像我一样),请看这里:http://stackoverflow.com/questions/5506339/how-can-i-git-stash-a-specific -文件 (68认同)
  • 我发现这种方法要简单得多:http://stackoverflow.com/a/5506483/457268 (46认同)
  • @Raman:太棒了!`git stash -p`正是我想要的.我想知道这个开关是否最近才被添加. (27认同)
  • 警告:`git stash --keep-index`已损坏.如果你进行了更多的更改,那么稍后你会尝试`git stash pop`你会遇到合并冲突,因为存储包含你保存的已更改文件,而不仅仅是你没有保留的文件.例如:我更改文件A和B,然后存储B,因为我想测试A中的更改; 我发现A的问题然后我解决了; 我承诺A; 现在我无法解除因为旧版本的A存在于存储中,没有充分的理由导致合并冲突.在实践中,A和B可能是许多文件,甚至可能是二进制图像或其他东西,所以我基本上不得不放弃并丢失B. (13认同)
  • 除了没有回答这个问题外,还有误导性.说"这将隐藏你以前没有添加过的所有东西." 建议当你"git add"一些文件时,它不会被隐藏起来.事实并非如此.无法理解为什么这被标记为答案而且有这么多的赞成. (5认同)
  • @Pistos我怀疑这个答案曾经是正确的,但是在写完之后不久,Git的一些版本就破坏了这个功能.https://git-scm.com/docs/git-stash上的**测试部分提交**示例清楚地表明`--keep-index`的*预期*行为正是这个答案所声称的,但是正如你所指出的,*实际*行为完全不同.然而它已经多年没有固定了!真是一团糟. (2认同)
  • 啊哈哈令人难以置信的是,一个与 OP 提出的完全不同的问题的答案是如何得到 +1000 的,哈哈:) 问题是关于`git stash -p` (2认同)

Von*_*onC 411

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

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

git stash push [--] [<pathspec>...]
Run Code Online (Sandbox Code Playgroud)

pathspec赋予' git stash push'时,新的存储仅记录与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
Run Code Online (Sandbox Code Playgroud)

最初的答案(下面,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
Run Code Online (Sandbox Code Playgroud)

可能更适合.


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

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

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

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

  • @VonC:每个答案只有一个答案,这是一种很好的风格.此外,将他人的答案复制粘贴到你自己的答案是不礼貌的. (5认同)
  • 这很好,但我经常修改了很多文件,所以使用补丁很烦人 (3认同)
  • @bukzor:如果我编辑的答案看起来不合适,我很抱歉.我唯一的目的是让你的答案更具可见性.我已经再次编辑了我的帖子,以便让这个意图更清晰. (3认同)
  • `git基本上是关于管理所有存储库内容和索引而不是一个或几个文件` - 这个实现掩盖了正在解决的问题; 这是一种解释,但不是理由.任何源控制系统都是关于"管理多个文件"的.看看哪些评论得到最多投票. (3认同)
  • 这也隐藏了所有暂存文件。所以请确保您没有进行任何更改 (2认同)

blu*_*yed 83

git stash -p(或git add -pstash --keep-index)将是太麻烦了,我发现它更容易使用diff,checkoutapply:

仅"隐藏"特定文件/目录:

git diff path/to/dir > stashed.diff
git checkout path/to/dir
Run Code Online (Sandbox Code Playgroud)

然后是

git apply stashed.diff
Run Code Online (Sandbox Code Playgroud)

  • 请注意,如果您有二进制文件(如PNG),它们将不会输出到diff文件.所以这不是100%的解决方案. (11认同)
  • 如果要存储的文件类似于`../../ foo/bar.txt`,这对我来说并不干净.补丁生成OK,但我需要移动到存储库根目录以获取要应用的补丁.因此,如果您遇到此问题 - 只需确保您从存储库根目录执行此操作. (2认同)

san*_*rom 77

使用git stash push,像这样:

git stash push [--] [<pathspec>...]
Run Code Online (Sandbox Code Playgroud)

例如:

git stash push -- my/file.sh
Run Code Online (Sandbox Code Playgroud)

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

  • @NikhilVandanapu我认为使用术语pathspec是因为它可以不仅仅是一个简单的路径。它可以包括标准通配符、双星号通配符甚至更深奥的语法。有关更多信息,请访问 [https://git-scm.com/docs/gitglossary](https://git-scm.com/docs/gitglossary) 并搜索 pathspec。 (5认同)
  • @VonC 你是对的,你也提到了正确的答案,但是,在两个答案之间,这个更容易阅读(没有混乱的文字,还有一个例子)。也许他们应该修改你的答案 (3认同)
  • 谁能告诉我为什么使用这样一个晦涩的词“pathspec”来表示文件路径?或者说它并不像我想象的那么晦涩难懂? (3认同)
  • 但我确实在去年 3 月,也就是 5 个月前的[我上面的回答](/sf/answers/212873881/)中提到了“git stash push”。我在这里详细介绍了新的 Git 2.13 命令:/sf/answers/3007452451/。 (2认同)
  • 然后是否使用“git stash apply”来恢复隐藏的更改? (2认同)

ven*_*ddy 46

假设你有3个文件

a.rb
b.rb
c.rb
Run Code Online (Sandbox Code Playgroud)

并且你想只存储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
Run Code Online (Sandbox Code Playgroud)

你完成了!HTH.


Jas*_*per 28

另一种方法:

# 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">
Run Code Online (Sandbox Code Playgroud)

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

这个想法与@VonC建议使用存储库外部文件的想法相同,您可以保存您想要的更改,删除存储中不需要的更改,然后重新应用您移动的更改.但是,我使用git stash作为"某处"(结果,最后还有一个额外的步骤:删除你存放在存储中的cahnges,因为你也将它们移开了).

  • @BryanAsh嗯,这不重要.我正在讲一个轶事,而不是真正提到其他答案.消息是我不喜欢社区喜欢的答案,而不是这些答案实际包含的答案.此外,第二个和第三个答案之间的900投票差距使得这在不久的将来不太可能改变,如果它应该改变,我总是可以编辑它来说"当时答案的顶部".真的,我不知道在这种情况下这是怎么回事. (2认同)

Jes*_*eke 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
Run Code Online (Sandbox Code Playgroud)

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

# <hack hack hack>
git add <files that you want to stash>
git stash-index
Run Code Online (Sandbox Code Playgroud)

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

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


Ami*_*aei 19

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

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


sha*_*iao 18

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

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

sea*_*cal 13

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

$ git stash -- filename.ext
Run Code Online (Sandbox Code Playgroud)

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

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

  • 这就是我一直在寻找的答案。谢谢!+1 (7认同)
  • 这个答案很冗长,这个很简洁。如果它对某人有帮助,我就会离开它。本页上没有人提到这种语法和结果 - 他们反而提到了“git stash push”。 (2认同)

小智 12

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

git checkout -- <file>
Run Code Online (Sandbox Code Playgroud)

[ 注意 ]

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


apr*_*cot 11

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

$ chmod +x stash
$ stash .*.xml
$ stash xyz.xml
Run Code Online (Sandbox Code Playgroud)

要复制到文件中的代码:

#! /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
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 好方法.我会选择这个作为答案.未来读者的提示:您必须在完整路径上进行匹配.例如stash subdir/foo.c (2认同)

vin*_*uja 10

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

git stash push "filename"
Run Code Online (Sandbox Code Playgroud)

或带有可选消息

git stash push -m "Some message" "filename"
Run Code Online (Sandbox Code Playgroud)

  • 这没有增加任何新内容。多个答案中已经提到了 Git stash push (2认同)

ele*_*ram 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的"中间"建议)


voi*_*ter 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>...
Run Code Online (Sandbox Code Playgroud)

注意第一个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>...
Run Code Online (Sandbox Code Playgroud)

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


Tri*_*nko 7

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

  1. 暂时提交你不想要藏匿的所有东西.
  2. Git添加其他所有内容,然后存储它.
  3. 通过运行git reset来弹出临时提交,在临时提交之前定位提交.

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


Raf*_*ian 7

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

  • 惊讶于这个答案的支持很少,这是最好的解决方案而不需要一篇文章. (3认同)

Chr*_*cci 6

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

这个"藏匿"怎么样:

git diff /dir/to/file/file_to_stash > /tmp/stash.patch
git checkout -- /dir/to/file/file_to_stash
Run Code Online (Sandbox Code Playgroud)

这将弹出文件更改回来:

git apply /tmp/stash.patch
Run Code Online (Sandbox Code Playgroud)

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


Zim*_*biX 6

我已经查看了此主题以及许多类似主题的答案和评论。请注意,以下命令都不正确,无法存储任何特定的跟踪/未跟踪文件

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

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

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

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