当我暂存差异但不提交差异时,如何取消"git stash pop"?



示例。我写了一些代码,上演了我想做的承诺,但我做到了";git stash pop"意外地在藏匿处,文件也被暂存。有没有一种方法可以取消我在";pop";然后留下我之前写的和上演的代码?

TL;DR

简单的答案是,你可能需要做很多工作,或者只是做一点工作。没有自动的方法来取回你所拥有的。

Long

示例。我写了一些代码,上演了我想做的承诺。。。

到目前为止,一切都很好。不过,在我进入下一步之前,我想确切地描述一下这一切的含义。

Git存储库的主要部分由两个数据库组成。一个保存所有提交和其他内部Git对象,另一个保存名称:分支名称、标记名称和其他此类名称。这些名称主要用于查找提交。每个提交都有编号,有一个唯一的(又大又丑)哈希ID,它看起来是随机的,所以我们需要一些机制来找到特别有趣的提交,比如某个分支的最新提交。提交本身存储其他提交的提交编号,因此一旦我们找到一些特别有趣的提交——例如最新的masterdevelop提交——我们就可以使用该提交来找到其他有趣的提交;但我们需要一个特定提交的哈希ID才能开始。

记住所有这些,还要记住,每次提交都是完全、完全、100%只读的。一旦您进行了提交,任何提交的任何内容都无法更改。而且,每次提交都会存储每个文件的完整存档。为了使这个档案不会很快用完所有的磁盘空间,提交中存储的文件是一种特殊的、只读的、仅限Git的、压缩的和消除重复的形式

这一切意味着你实际上不能使用任何提交。或者更确切地说,在你第一次拥有Gitun-archive之前,你不能使用它,将其保存的归档文件转换回普通文件。这就是git checkout或新的(自Git 2.23以来)git switch的作用:你告诉Git给我最新的master提交让我最新develop提交的存档来填充一个充满可用文件的工作树。

Git调用这个区域,以及您可以处理的可用文件,以及您的工作树。你在这里工作,在你的工作树上。

工作树文件不是Git存储库中的文件它们是副本,由这些文件制成;但是Git存储库中的文件采用不同的、仅限Git的格式当你进行新的提交时,Git将不得不重新对所有文件进行Gitize,而且这个过程会非常缓慢,所以Git不会只是重新进行相反,Git会保留上次提交以来的所有Git-ized文件。这些位于Git所称的索引暂存区中,或者(现在很少)缓存最初,此处缓存的副本与您签出的提交完全匹配

在工作树上工作时,缓存的索引副本工作树副本不同步。它们仍然与旧的已提交副本相匹配,因为它们尚未更改。

当您运行git add时,Git所做的将替换缓存的索引副本。Git读取文件的工作树副本,对其进行压缩和消除重复,将其转换为仅限Git的格式,并将其放入缓存。这将替换在索引/暂存区域中的旧副本。新的已准备好提交。(旧的也是:只是它与您签出的提交副本匹配!)

一旦某个文件的索引副本与提交的副本不同,Git就会开始调用该文件staged for commit。然而,事实上,Git的索引/暂存区中的每个文件都有。只是其中一些文件仍然与现有的提交文件匹配:它们是预消除重复的重复文件。使用git add更新的其他文件也已预消除重复:如果它们与存储库中任何位置的已提交文件匹配,则现在提交将重复使用旧文件。如果它们是全新的,那么现在提交将向存储库的对象数据库添加一个全新的文件(从技术上讲,是一个全新blob对象)。

因此:暂存一些文件实际上意味着制作新的、预消除重复的副本,并用新的索引副本替换旧的预消除重复索引副本

。。。但我做到了";git stash pop"意外地

现在我们进入棘手的部分。git stash命令本身,当它进行存储时,实际上只是进行一些提交。它会进行两次提交,有时会进行三次。这些提交的特殊之处在于,它们不在任何分支上。但除此之外,它们只是提交:运行git stash save会产生几个新的提交。与任何提交一样,这两个提交包含每个文件的完整快照。棘手的部分是这两个提交中的是什么,以及它们在之后如何与git stash popgit stash apply一起使用。

在存储中,文件也被暂存。

根据定义,这是错误的:说某个文件是暂存的意味着文件的索引副本与当前提交副本不同。但两个存储提交中的所有文件都在提交中。因此,藏匿处本身中的任何都不是暂存的。索引本身独立于两个存储提交。

但是:当您运行git stash pop时,您必须选择如何处理这两个存储提交。您可以选择完全忽略第一个提交,这是默认设置。只有运行git stash pop --index,Git才会同时使用提交。

现在是时候看看一些存储中的两个提交,以及它们在git stash applygit stash pop:期间是如何应用的了

  • 两个提交中的第一个与运行git commit时得到的结果相匹配:它包含运行git stash时Git索引中的所有文件。如果确实运行git stash pop --index,Git会尝试以相当复杂的方式将此提交应用于当前索引内容。这要么在没有冲突的情况下成功,要么什么都不发生,Git说无法应用隐藏(整个git stash pop --index被中止)。

    (如果使用--index并且在这里成功,Git此时将结果放在临时区域中,因为下一步需要使用索引,Git现在重置索引,就像使用git reset --mixed一样。)

  • 两次提交中的第二次与运行git add -u然后运行git commit时得到的结果相匹配:它包含了工作树中的所有文件,这些文件在Git的索引中具有相应的文件。假设您省略了--index,或者成功应用了索引提交,Git现在继续运行git merge-recursive。这将尝试将工作树的当前内容与第二次提交中的内容合并,使用第二次的父提交作为合并基础。

    注意,与常规git merge不同,此步骤不要求您的工作树为";清洁";。如果此合并失败,Git将git stash pop转换为git stash apply(即,不丢弃存储),并停止处理工作树和Git索引中留下的任何混乱这可能无法以任何自动方式恢复

无论如何,一旦你完成了这一点,是时候提到在藏匿处进行第三次提交的可能性了在进行隐藏时,可以使用-u-a选项将未跟踪的文件放入第三次提交。如果选择此选项,git stash applygit stash pop将尝试在早期恢复此第三次提交。如果失败,git stash将根本不会在工作树上运行git merge-recursive,实际上什么都不会发生。没有跳过第三次提交的选项:如果存在,则使用它,如果不存在,则不使用它。git stash list可能应该用关于第三次提交是否存在的内容来注释列表,git stash show应该在这里提到一些内容,但它们没有。所以你必须记住你是否做了任何三个承诺藏匿。

现在,让我们假设正常的两个提交存储,您没有在此处运行git stash pop --index

有没有办法取消我在";pop";然后留下我之前写的和上演的代码?

如果pop操作认为它已成功完成,则将运行git stash drop作为其最后一步。这丢弃了隐藏提交,由于它们根本不在任何分支上,因此在这之后的任何时候,它们都可能被垃圾收集并永远丢弃我建议避免使用git stash pop请改用git stash apply不会运行git stash drop。然后,一旦您确定存储被正确应用并且您不想要它,就单独运行git stash drop

如果pop操作认为出了问题——因为您遇到了合并冲突——那么您仍然有存储本身。不幸的是,您已经失去了之前精心安排的文件,因为在存储中使用工作树提交的git merge-recursive步骤修改了Git的索引。合并发生在索引中,当发生冲突时,使用工作树作为额外的暂存空间。

如果您在这个git stash pop之前运行git stash,那么您将根据Git索引中的内容进行提交,并且该提交将包含您想要的内容。当然,您必须以其他方式处理其他存储,并且由于git stash push/git stash savegit reset --hard或等效值结尾,因此您还需要首先运行git stash apply --index。但你没有做这些,所以在git merge-recursive步骤之后,你就有了Git留给你的任何东西。

我自己的建议是尽可能避免git stash。请记住,它通过提交来工作。这些提交很难看到(因为它们在no分支上),也很难使用(因为只有git stash命令本身才能正确使用它们),而且它们不会做任何正常日常提交无法做的事情。日常提交易于查看和使用。Stash确实有一个优势,那就是因为它所做的提交是在no分支上进行的,所以在一个分支上进行提交并将其应用于另一个分支很容易,而无需考虑,但这一优势非常小——不必考虑部分会导致没有考虑用不干净时,这会导致痛苦。

是的,有一种方法可以撤消您所做的操作。

来自git stash手册页:

push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]

将您的本地修改保存到一个新的存储条目中,并将它们回滚到HEAD(在工作树和索引中)。<message>部分是可选的,它提供了描述以及隐藏状态。

为了快速制作快照,您可以省略";"推";。在这种模式下,不允许使用非选项参数来防止拼写错误的子命令生成不需要的隐藏条目。有两个例外是stash-p它充当隐藏push-p和pathspec元素的别名,这两个元素在双连字符之后是允许的,以消除歧义。

您只需要git stash push -- <files>就可以将它们推送到新的存储库中。既然你并不是说你有任何冲突,我想你只需要记住什么需要放在藏匿处,什么需要留在集结区。

如果你不确定要保存什么和隐藏什么,那么你可以在控制台中检查git stash pop的输出,因为它会告诉你你弹出的隐藏的sha1:

Dropped refs/stash@{0} (fcb1753544d849af2548bafa48c4d74debbda518)

编辑/注意:如果确实遇到冲突,那么git将在stash pop阶段投诉,您将不得不解决所述冲突。Git会保存存储:)所以在这种情况下,你不需要担心丢失存储。

最新更新