有没有办法只存储我的分阶段更改?我遇到问题的场景是我在给定时间处理了几个错误,并且有几个未分阶段的更改。我希望能够单独暂存这些文件,创建我的 .patch 文件并将它们隐藏起来,直到代码获得批准。这样,当它被批准时,我可以隐藏我的整个(当前)会话,弹出那个错误并推送代码。
我会以错误的方式解决这个问题吗?我是否误解了 git 如何以其他方式简化我的流程?
是的,可以使用DOUBLE STASH
git stash --keep-index
。此命令将创建一个包含所有更改(暂存和未暂存)的存储,但会将暂存的更改保留在您的工作目录中(仍处于暂存状态)。git stash push -m "good stash"
"good stash"
只有暂存文件。现在,如果您在 stash 之前需要未暂存的文件,只需应用第一个 stash(使用 创建的那个--keep-index
),现在您可以删除您隐藏的文件"good stash"
。
享受
使用最新的 git,您可以使用--patch
选项
git stash push --patch # since 2.14.6
git stash save --patch # for older git versions
并且 git 会询问您文件中的每个更改是否添加到存储中。
您只需回答y
或n
DOUBLE STASH的UPD
别名:
git config --global alias.stash-staged '!bash -c "git stash --keep-index; git stash push -m "staged" --keep-index; git stash pop stash@{1}"'
现在您可以暂存文件,然后运行git stash-staged
.
结果,您的暂存文件将保存到 stash中。
如果您不想保留暂存文件并将它们移动到存储中。然后你可以添加另一个别名并运行git move-staged
:
git config --global alias.move-staged '!bash -c "git stash-staged;git commit -m "temp"; git stash; git reset --hard HEAD^; git stash pop"'
TL;DR只需
-- $(git diff --staged --name-only)
为您的 git<pathspec>
参数添加
git stash -- $(git diff --staged --name-only)
并简单地添加一条消息:
git stash push -m "My work in progress" -- $(git diff --staged --name-only)
在v2.17.1和v2.21.0.windows.1上测试
限制:
- 请注意,如果您没有暂存文件,这将隐藏每一件事。
- 此外,如果您有一个仅部分暂存的文件(即只有一些更改的行被暂存,而其他一些更改的行没有),那么整个文件将被隐藏(包括未暂存的行)。
我制作了一个脚本,它只存储当前上演的内容并保留其他所有内容。当我开始进行太多不相关的更改时,这真是太棒了。简单地暂存与所需提交无关的内容并将其隐藏。
(感谢 Bartłomiej 的起点)
#!/bin/bash
#Stash everything temporarily. Keep staged files, discard everything else after stashing.
git stash --keep-index
#Stash everything that remains (only the staged files should remain) This is the stash we want to keep, so give it a name.
git stash save "$1"
#Apply the original stash to get us back to where we started.
git stash apply stash@{1}
#Create a temporary patch to reverse the originally staged changes and apply it
git stash show -p | git apply -R
#Delete the temporary stash
git stash drop stash@{1}
为了完成同样的事情......
git commit -m 'temp'
git add .
git stash
git reset HEAD~1
繁荣。您不想要的文件被隐藏起来。你想要的文件都准备好了。
在 Git 2.35(2022 年第一季度)中,“ git stash
” (man)学会了--staged
隐藏已添加到索引中的内容的选项(仅此而已)。
所以现在正式支持(8 年后)。
请参阅Sergey Organov ( ) 的提交a8a6e06 ( 2021 年 10 月 28 日)和提交 41a28eb(2021 年 10 月 18 日)。(由Junio C Hamano 合并——在提交 44ac8fd中,2021 年 11 月 29 日)sorganov
gitster
stash
: 为 'push' 和 'save' 实现 '--staged' 选项签字人:谢尔盖·奥加诺夫
仅存储暂存的更改。
此模式允许轻松隐藏以供以后重用与当前正在进行的工作无关的一些更改。
与 '
stash push --patch
' 不同,--staged
支持使用任何工具来选择要隐藏的更改,包括但不限于 'git add --interactive
' ( man )。
git stash
现在在其手册页中包含:
'git stash' [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]
git stash
现在在其手册页中包含:
save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]
git stash
现在在其手册页中包含:
-S
--staged
此选项仅对
push
和save
命令有效。仅存储当前暂存的更改。这类似于基本
git commit
的,除了状态被提交到存储而不是当前分支。该
--patch
选项优先于该选项。
git stash
现在在其手册页中包含:
Saving unrelated changes for future use
当您正在进行大规模更改并且发现一些您不想忘记修复的不相关问题时,您可以进行更改,将它们暂存并用于
git stash push --staged
将它们隐藏起来以备将来使用。
这类似于提交分阶段的更改,只是提交最终在存储中,而不是在当前分支中。---------------------------------------------------------------- # ... hack hack hack ... $ git add --patch foo # add unrelated changes to the index $ git stash push --staged # save these changes to the stash # ... hack hack hack, finish curent changes ... $ git commit -m 'Massive' # commit fully tested changes $ git switch fixup-branch # switch to another branch $ git stash pop # to finish work on the saved changes ----------------------------------------------------------------
2022 年 1 月更新:Git 2.35 已发布,stash
现在支持--staged
参数。因此,此答案对于 Git 2.35+ 已过时。请参阅 vonc 的回答:https ://stackoverflow.com/a/70231955/430128 。
老答案:
在 Git 中仅存储索引(分阶段更改)比应有的困难。我发现@Joe 的答案运行良好,并将它的一个小变体变成了这个别名:
stash-index = "!f() { \
! git diff --cached --exit-code --quiet && \
git stash push --quiet --keep-index -m \"temp for stash-index\" && \
git stash push \"$@\" && \
git stash pop --quiet stash@{1} && \
git stash show -p | git apply -R; }; f"
它:
验证是否存在实际分阶段的更改(git diff --cached --exit-code
如果有则返回非零状态)。HT:@nandilugio
它将分阶段和非分阶段的更改都推送到临时存储中,而单独保留分阶段的更改。
然后它将分阶段的更改推送到存储中,这是我们想要保留的存储。传递给别名的参数,例如--message "whatever"
将添加到此存储命令中。
它弹出临时存储以恢复原始状态并删除临时存储,然后
最后通过反向补丁应用程序从工作目录中“删除”隐藏的更改。
对于仅存储未暂存更改(别名stash-working
)的相反问题,请参见此答案。
在这种情况下,我更喜欢为每个问题创建新的分支。我使用前缀 temp/ 所以我知道以后可以删除这些分支。
git checkout -b temp/bug1
暂存修复 bug1 的文件并提交它们。
git checkout -b temp/bug2
然后,您可以根据需要从各个分支中挑选提交并提交拉取请求。
为什么不提交某个错误的更改并从该提交及其前身创建一个补丁?
# hackhackhack, fix two unrelated bugs
git add -p # add hunks of first bug
git commit -m 'fix bug #123' # create commit #1
git add -p # add hunks of second bug
git commit -m 'fix bug #321' # create commit #2
然后,要创建适当的补丁,请使用git format-patch
:
git format-patch HEAD^^
这将创建两个文件:0001-fix-bug-123.patch
和0002-fix-bug-321.patch
或者,您可以为每个错误创建单独的分支,这样您就可以单独合并或重新调整错误修复,如果它们不起作用,甚至删除它们。
git stash --keep-index
是一个很好的解决方案...除了它在已删除的路径上无法正常工作,这已在 Git 2.23(2019 年第三季度)中修复
请参阅Thomas Gummerer ( ) 的提交 b932f6a(2019 年 7 月 16 日)。(由Junio C Hamano 合并——在提交 f8aee85中,2019 年 7 月 25 日)tgummerer
gitster
stash
:修复处理删除的文件--keep-index
git stash push --keep-index
应该将所有已添加到索引中的更改保留在索引和磁盘中。目前,当从索引中删除文件时,这不会正确运行。**--keep-index当前恢复文件
,而不是将其保留在磁盘上。**
git checkout
通过在无覆盖模式下使用“”来修复该行为,可以忠实地恢复索引和工作树。
这也简化了代码。请注意,如果未跟踪的文件与已在索引中删除的文件同名,这将覆盖未跟踪的文件。
另一种方法是使用您不想隐藏的文件创建一个临时提交,然后隐藏剩余的文件并轻轻删除最后一次提交,保持文件完整:
git add *files that you don't want to be stashed*
git commit -m "temp"
git stash --include-untracked
git reset --soft HEAD~1
这样你就只能触摸你想要触摸的文件。
请注意,这里使用“--include-untracked”来存储新文件(这可能是您真正想要的)。
是否绝对有必要同时处理多个错误?“一次”是指“同时为多个错误编辑文件”。因为除非您绝对需要,否则我一次只会在您的环境中处理一个错误。这样你就可以使用本地分支和变基,我发现这比管理复杂的存储/阶段要容易得多。
假设 master 在提交 B。现在处理 bug #1。
git checkout -b bug1
现在你在分支 bug1 上。进行一些更改,提交,等待代码审查。这是本地的,因此您不会影响其他任何人,并且从 git diffs 制作补丁应该很容易。
A-B < master
\
C < bug1
现在你正在处理 bug2。回去掌握。_ git checkout master
新建一个分支,git checkout -b bug2
. 进行更改,提交,等待代码审查。
D < bug2
/
A-B < master
\
C < bug1
让我们假设其他人在您等待审查时在 master 上提交了 E&F。
D < bug2
/
A-B-E-F < master
\
C < bug1
当您的代码获得批准后,您可以通过以下步骤将其重新设置为 master:
git checkout bug1
git rebase master
git checkout master
git merge bug1
这将导致以下结果:
D < bug2
/
A-B-E-F-C' < master, bug1
然后你可以推送,删除你的本地 bug1 分支,然后你就可以走了。在您的工作空间中一次出现一个错误,但通过使用本地分支,您的存储库可以处理多个错误。这避免了复杂的舞台/隐藏舞。
在评论中回答ctote的问题:
好吧,您可以为每个错误返回 stash,并且一次只处理一个错误。至少可以为您节省分期问题。但是试过这个,我个人觉得很麻烦。在 git 日志图中,Stash 有点混乱。更重要的是,如果你把事情搞砸了,你就无法恢复。如果你有一个脏的工作目录并且你弹出一个存储,你不能“撤消”那个弹出。搞砸已经存在的提交要困难得多。
所以git rebase -i
。
当您将一个分支变基到另一个分支时,您可以交互地进行(-i 标志)。执行此操作时,您可以选择要对每个提交执行的操作。Pro Git 是一本很棒的书,它也以 HTML 格式在线,并且有一个关于 rebase 和 squashing 的不错的部分:
http://git-scm.com/book/ch6-4.html
为方便起见,我将逐字窃取他们的示例。假设您有以下提交历史记录,并且您想将 bug1 变基并压缩到 master 上:
F < bug2
/
A-B-G-H < master
\
C-D-E < bug1
这是您在键入时将看到的内容git rebase -i master bug1
pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file
#
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
要将分支的所有提交压缩为单个提交,请将第一个提交保留为“pick”并将所有后续“pick”条目替换为“squash”或简单的“s”。您也将有机会更改提交消息。
pick f7f3f6d changed my name a bit
s 310154e updated README formatting and added blame
s a5f4a0d added cat-file
#
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
所以,是的,squashing 有点痛苦,但我仍然会推荐它而不是大量使用 stash。
在您对 Mike Monkiewicz 回答的评论中,我建议使用更简单的模型:使用常规开发分支,但使用合并的 squash 选项在您的主分支中获得单个提交:
git checkout -b bug1 # create the development branch
* hack hack hack * # do some work
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
* hack hack hack *
git commit
git checkout master # go back to the master branch
git merge --squash bug1 # merge the work back
git commit # commit the merge (don't forget
# to change the default commit message)
git branch -D bug1 # remove the development branch
此过程的优点是您可以使用正常的 git 工作流程。
要删除意外更改,尤其是删除多个文件,请执行以下操作:
git add <stuff to keep> && git stash --keep-index && git stash drop
换句话说,把垃圾藏起来,把它和藏起来的东西一起扔掉。
在 git 版本 2.17.1 中测试
我还没有看到不需要使用的解决方案git stash
:
你甚至根本不需要使用git stash
。您可以使用此处介绍的专用分支来解决此问题(分支很便宜)。
实际上,您可以使用一些可以捆绑到一个 git 别名中的连续命令来单独隔离未执行和暂存的更改:
创建并切换到一个新分支,您将在其中分别提交暂存和未暂存的更改:请参阅此处
在任何时候,您都可以git cherry-pick -e
从已创建的分支提交一次以将其应用到您想要的位置(-e
以更改其提交消息)。
当你不再需要它时,你可以删除这个“stash 分支”。您可能必须使用-D
强制删除选项(而不是-d
普通选项),因为所述分支未合并,并且 git 可能会认为如果您删除它可能会丢失数据。如果您在删除之前没有精心挑选提交,那就是真的:
git branch -D separated-stashes
您还可以为您添加别名~/.gitconfig
以自动执行此行为:
git config --global alias.bratisla '!git switch -c separated-stashes; git commit -m "staged changes"; git add -u; git commit -m "unstaged changes"; git switch -' # why this name ? : youtu.be/LpE1bJp8-4w
当然,您也可以使用两个连续的 stash来达到相同的结果
git stash (-k|--keep-index)
如其他答案所述,您有一些方法可以与其他命令结合使用仅存储未暂存或仅暂存的更改。
我个人觉得该-k
选项非常令人困惑,因为它隐藏了所有内容,但将分阶段更改保持在分阶段状态(这解释了为什么“ --keep-index
”)。而存储某些东西通常会将其移动到存储条目。-k
未暂存的更改通常被隐藏,但暂存的更改只是复制到同一个隐藏条目。
第 0 步:您的 git status 中有两件事:一个包含暂存更改的文件,另一个包含未暂存更改的文件。
第 1 步:存储未分级 + 分级更改,但将分级更改保留在索引中:
git stash -k -m "all changes"
该-m "..."
部分是可选的,git stash -k
实际上是一个别名git stash push -k
(它不会远程推送任何东西,顺便说一句,别担心),它接受一个-m
选项来标记您的存储条目以清楚起见(如提交消息或标签,但用于存储条目)。它是 deprecated 的较新版本git stash save
。
步骤 1 之二(可选):
git stash
存储分阶段的更改(仍在索引中)。此步骤对于以下内容不是必需的,但表明您可以根据需要仅将暂存的更改放入存储条目中。如果您使用此行,则必须git stash (pop|apply) && git add -u
在继续步骤 2 之前。
第2步 :
git commit -m "staged changes"
提交仅包含步骤 0 中的暂存更改,它包含与步骤 1bis 中的存储条目相同的内容。
第 3 步:
git stash (pop|apply)
从第 1 步恢复存储。请注意,此存储条目包含所有内容,但由于您已经提交了分阶段更改,此存储将仅添加来自第 0 步的未分阶段更改。
注意:这里的“restore”并不意味着“git restore”,这是一个不同的命令。
第4步 :
git add -u
将弹出的存储内容添加到索引
第 5 步:
git commit -m "unstaged changes"
此处的“未暂存”,如第 2 步和第 3 步的注释中的“暂存”,是指第 0 步。您实际上是在暂存并提交第 0 步中的“暂存更改”。
完毕 !您现在有两个单独的提交,其中包含来自步骤 0 的(未)暂存更改。您可能希望修改/重新设置它们以进行其他更改或重命名/删除/压缩它们。根据您对存储堆栈 (pop
或apply
) 所做的操作,您可能还想要git stash (drop|clear)
它。你可以看到你存储条目git stash (list|show)
TL;博士;
git stash-staged
创建别名后:
git config --global alias.stash-staged '!bash -c "git stash -- \$(git diff --staged --name-only)"'
这里git diff
返回--staged
文件列表--name-only
然后我们将这个列表传递pathspec
给git stash
commad。
来自man git stash
:
git stash [--] [<pathspec>...]
<pathspec>...
The new stash entry records the modified states only for the files
that match the pathspec. The index entries and working tree
files are then rolled back to the state in HEAD only for these
files, too, leaving files that do not match the pathspec intact.