Stash only one file out of multiple files that have changed before git 2.13
如何在我的分支上只存储多个已更改文件中的一个?
您也可以使用
您将被提示为每个Hunk执行一些操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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。对于Git2.13及更高版本,请查看如何Git存储特定文件?
警告
正如评论中所指出的,这将所有东西都放进了藏匿处,无论是阶段性的还是非阶段性的。保存索引只会在隐藏完成后离开索引。当您稍后弹出隐藏时,这可能会导致合并冲突。
这将存储您以前没有添加的所有内容。只要你想保留的东西,然后运行它。
1 | git stash --keep-index |
例如,如果要将旧提交拆分为多个变更集,可以使用以下过程:
由于git从根本上是关于管理所有存储库内容和索引(而不是一个或多个文件),因此
实际上,由于Git2.13(2017年第2季度),您可以使用
1 | git stash push [--] [<pathspec>...] |
When
pathspec is given to 'git stash push ', the new stash records the modified states only for the files that match the pathspec
有关更多信息,请参阅"隐藏对特定文件的更改"。
测试用例是不言而喻的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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评论:
This (the
stash --patch original solution) is nice, but often I've modified a lot of files so using patch is annoying
Bukzor的回答(upvoted,2011年11月)提出了一个更实际的解决方案,基于
关于这个选项,chhh在评论中指出了另一个工作流:
you should"
git reset --soft " after such a stash to get your clear staging back:
In order to get to the original state - which is a clear staging area and with only some select un-staged modifications, one could softly reset the index to get (without committing anything like you - bukzor - did).
(原始答案2010年6月:手动储存)
然而,
With
--patch , you can interactively select hunks from in the diff between HEAD and the working tree to be stashed.
The stash entry is constructed such that its index state is the same as the index state of your repository, and its worktree contains only the changes you selected interactively. The selected changes are then rolled back from your worktree.
但是,这将保存完整索引(可能不是您想要的,因为它可能包括其他已经索引的文件)和部分工作树(可能看起来像您想要存储的工作树)。
1 | git stash --patch --no-keep-index |
可能更合适。
如果
对于一个或多个文件,中间解决方案是:
- 在Git回购协议之外复制它们(实际上,Eleotlecram提出了一个有趣的替代方案)
git stash - 把它们抄回去
git stash 这一次,只有你想要的文件才被保存起来。git stash pop stash@{1} 重新应用所有文件修改- 在进行任何本地修改之前,
git checkout -- afile 将文件重置为头内容
在这个相当繁琐的过程结束时,您将只有一个或多个文件被保存起来。
当
仅"存储"特定文件/dir:
1 2 | git diff path/to/dir > stashed.diff git checkout path/to/dir |
然后之后
1 | git apply stashed.diff |
使用
1 | git stash push [--] [<pathspec>...] |
例如:
1 | git stash push -- my/file.sh |
自2017年春季发布的Git2.13起,该版本就开始提供。
假设你有3个文件
1 2 3 | a.rb b.rb c.rb |
你只想储存b.rb和c.rb,而不是a.rb
你可以这样做
1 2 3 4 5 6 7 8 9 10 | # 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.
另一种方法是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # 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"> |
在我(再一次)来到这一页,不喜欢前两个答案(第一个答案不回答问题,我不太喜欢使用
这个想法与@vonc建议使用存储库外的文件时的想法相同,您可以将所需的更改保存到某个位置,删除存储库中不需要的更改,然后重新应用您移出的更改。但是,我把git stash作为"某处"(结果,在结尾还有一个额外的步骤:去掉你放在stash中的cahnges,因为你也把它们移走了)。
更新(2015年2月14日)-我已经重写了一些脚本,以便更好地处理冲突的情况,现在应该将这些冲突显示为未合并的冲突,而不是.rej文件。
我经常发现对@bukzor的方法做相反的操作更直观。也就是说,阶段化一些更改,然后只存储那些阶段化的更改。
不幸的是,Git不提供Git存储——只提供索引或类似的内容,所以我快速编写了一个脚本来实现这一点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #!/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 |
您可以将上述脚本保存为路径上的某个位置的
1 2 3 | # <hack hack hack> git add <files that you want to stash> git stash-index |
现在,stash包含一个新条目,该条目只包含您所进行的更改,而您的工作树仍然包含任何未更改。
在某些情况下,工作树的更改可能取决于索引的更改,因此当您存储索引更改时,工作树的更改会产生冲突。在这种情况下,您将得到可以使用git merge/git mergetool/etc解决的常见未合并冲突。
因为在Git中创建分支很简单,所以您可以创建一个临时分支并将各个文件检入其中。
将以下代码保存到一个名为
1 2 3 | $ chmod +x stash $ stash .*.xml $ stash xyz.xml |
要复制到文件中的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #! /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 " } else { send"a " send_user"$filename " } } "Stash deletion" { send"n " } eof { exit } } } |
只是为了防止您在使用
1 | git checkout -- <file> |
[注释]
我会用
VONC将文件复制到git repo外部的"中间"解决方案存在的问题是,您会丢失路径信息,这使得稍后复制大量文件变得有些麻烦。
a发现更容易使用tar(类似的工具可能会做)而不是复制:
- tar cvf/tmp/stash.tar path/to/some/file path/to/some/other/file(…等)
- git签出路径/to/some/file path/to/some/other/file
- 暂存
- 焦油xvf/tmp/stash.tar
- 等(见VONC的"中间"建议)
使用sourcetree可以通过3个步骤轻松完成。
这一切都可以在sourcetree中几秒钟内完成,您只需单击要添加的文件(甚至是单独的行)。一旦添加,只需将它们提交到临时提交。接下来,单击复选框以添加所有更改,然后单击"存储"以存储所有内容。当隐藏的更改被排除在外时,浏览提交列表并在临时提交之前记录提交的哈希值,然后运行"git reset hash_b4_temp_commit",这基本上类似于"popping"提交,方法是在提交之前将分支重置为提交。现在,你只剩下那些你不想藏起来的东西了。
有时候我在提交分支之前对它做了一个不相关的更改,我想将它移动到另一个分支并单独提交(如master)。我这样做:
1 2 3 4 5 6 7 8 9 | 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>... |
注意:第一个
您可以简化它,假设没有冲突,也没有新的分支:
1 2 3 4 5 | git checkout master git add <files that you want to commit> git commit -m 'Minor feature' git checkout topic1 ...<resume work>... |
甚至不需要藏匿…
这里的每个答案都很复杂…
把这个"藏起来"怎么样?
1 2 | git diff /dir/to/file/file_to_stash > /tmp/stash.patch git checkout -- /dir/to/file/file_to_stash |
要弹出文件更改,请执行以下操作:
1 | git apply /tmp/stash.patch |
与保存一个文件并将其弹出完全相同的行为。
我已经回顾了这方面的答案和评论以及一些类似的主题。请注意,为了能够存储任何特定的跟踪/未跟踪文件,以下命令都不正确:
git stash -p (--patch) :手工选择hunks,不包括未跟踪的文件git stash -k (--keep-index) :将所有跟踪/未跟踪的文件存放在工作目录中git stash -u (--include-untracked) :存储所有跟踪/未跟踪的文件git stash -p (--patch) -u (--include-untracked) 命令无效
目前,能够存储任何特定跟踪/未跟踪文件的最合理方法是:
- 暂时提交不想保存的文件
- 添加和隐藏
- 弹出临时提交
我在另一个问题的答案中为这个过程编写了一个简单的脚本,这里有在sourcetree中执行这个过程的步骤。
解决方案
局部变化:
- 文件(已修改)未暂存
- 文件_B(已修改)未暂存
- 文件c(已修改)未暂存
要创建一个只有文件更改的存储"我的存储",请执行以下操作:
1 2 3 4 | 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} |
完成。
解释您可以在步骤之间使用git状态来查看发生了什么。
如果不想指定包含隐藏更改的消息,请在双破折号后传递文件名。
1 | $ git stash -- filename.ext |
如果这是一个未跟踪/新文件,你必须先准备好它。
此方法适用于Git版本2.13。+
1 2 3 4 5 | 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) |
在这种情况下,我把剩下的东西藏起来。
当您尝试在两个分支之间切换时,就会出现这种情况。
尝试使用"
稍后执行此行
git stash --keep-index
要存储单个文件,请使用
提示:
我不知道如何在命令行上执行,只使用sourcetree。假设您已经更改了文件A,并且在文件B中有两个更改块。如果您只想将第二个更改块存储在文件B中,而不影响其他所有内容,请执行以下操作:
类似情况。我已经承诺并意识到这不好。
1 2 | git commit -a -m"message" git log -p |
根据答案,这对我有帮助。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # 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 |
我找不到我需要的答案,这就像:
1 2 3 4 | git add -A git reset HEAD fileThatYouWantToStash git commit -m"committing all but one file" git stash |
这只存储了一个文件。
一个复杂的方法是首先提交所有内容:
1 2 | git add -u git commit // creates commit with sha-1 A |
重置回原始提交,但从新提交签出一文件:
1 2 | git reset --hard HEAD^ git checkout A path/to/the_one_file |
现在,您可以存储一个文件:
1 | git stash |
通过在重置回原始提交时保存文件系统中提交的内容进行清理:
1 2 | git reset --hard A git reset --soft HEAD^ |
是的,有点尴尬…
快速回答
要在git中恢复特定的更改文件,可以执行以下操作:
1 | git checkout <branch-name> -- <file-path> |
下面是一个实际的例子:
1 | git checkout master -- battery_monitoring/msg_passing.py |