繁体   English   中英

吉特:去做旧提交,现在我不能回来了

[英]Git: went to old commit, now I can't come back

我使用以下代码移至旧的提交,其中包含已删除的一些代码:

git checkout xxx .

但是现在我想回到工作所在,尝试了以下方法: 我没有保存或提交最新代码

git checkout xxx/xxxx .

xxx / xxx是我一直在工作的分支的名称。

但是我的文件没有更改,它们仍然包含所有旧代码,而没有包含我的新代码!

任何帮助将不胜感激。

恐怕par的答案可能是正确的答案 但是,如果您对文件进行git add (即使不进行git commit ,结果也可能会很幸运。 如果全部为“ TL; DR”,则可以跳到下面的最后一部分。

为了清楚起见,让我们首先注意到git checkout本质上有三种或四种形式: 1

  • git checkout branchname
  • git checkout commit-ish
  • git checkout commit-ish -- path [ path ... ]
  • git checkout -- path [ path ... ]

第一种形式,您为git checkout了一个分支名称, 并且没有路径参数 ,试图将您带入branch 例如, git checkout master会将您置于分支master (或者失败并且什么也不做)。

第二种形式,您给git checkout一些可以解析为特定提交ID的内容,但由于第一种形式,它不是普通的本地分支名称而是检出指定的提交并为您提供“分离的HEAD”。 (或者再次,它可能会失败并且什么都不做。)内部工作的方式是,您离开之前的分支,而现在位于新的临时匿名(未命名)分支上,该分支在提交时结束您刚刚退房。

第二种形式是查看旧提交或重建旧提交或签出标签并进行构建(或类似操作)的常用方法。 例如,您可以git checkout c0ffee1 (哈希ID)或git checkout v2.7.2 (标签)。 但这不是你所做的。

第三和第四种形式的动作非常不同。 如果我负责Git的世界,那么它们根本不会被拼写为git checkout ,因为它们不会带您进行另一次提交。 它们对HEAD没有影响。 如果您在分支上,则留在该分支上。 如果您处于具有特定提交的分离HEAD模式,那么您将处于具有相同特定提交的分离HEAD模式。 但是现在我们进入了第一个复杂性,因为现在我们必须谈论索引和工作树。

git checkout的前两种形式相对安全,因为它们确保您不会丢失索引和工作树中的文件。 您可以使用-f / --force标志来要求它们覆盖文件。 git checkout的第三和第四种形式并不安全。 Git认为每个path参数都是覆盖给定文件或目录的请求。

commit-ish参数是Git可以用来查找提交的任何内容。 这可以是原始哈希ID(例如c0ffee1 )或标签(例如v2.7.2 ,甚至是分支名称(例如master 请注意,我们将分支名称拆分为git checkout一种特殊(第一种)形式,但这仅在没有path参数的情况下适用 当您提供一些path参数时,像master这样的分支名称就不再特殊了。

顺便说一句, --之前的path是可选的。 存在它的原因是允许您使用类似于选项的path 例如,如果您有一个名为-f--force的文件,则需要能够告诉Git引用该文件 -f--force ,而不是使用-f--force选项。 如果您有一个名为master的文件,则需要能够告诉Git您所引用的是名为master文件 ,而不是名为master的分支。 如果省略-- ,Git会最好地猜测您是指分支名称,提交ID,选项还是文件名。 在您的情况下,您使用. ,绝对是文件名,所以git checkout xxx . 成为第三种形式。 当然, . 表示“整个当前目录,包括其所有文件和子目录”。


1表格的确切数量取决于您决定如何计算这些表格。 例如,您可以将前两个折叠为一种形式,然后将第二个折叠为另一种形式,或者将前两个分开并合并第三和第四种形式。 还有git checkout -mgit checkout -pgit checkout --oursgit checkout --theirs ,它们与这四个主要方法略有不同。 但是在开始使用它们之前,请不要担心它们。


提交,索引和工作树

承诺是Git存在的理由。 他们记录了您一直以来所做的(或至少已承诺的)所有事情,以便您可以取回它们。 每个提交都以树状结构(包含文件和子目录的目录等)的形式保存一些文件的完整快照。 它还保存先前(父)提交的ID,以及一些元数据,例如您的姓名和电子邮件地址,时间戳以及提交日志消息。 就是这样:提交就是保存的树加上一些元数据。

工作树也很明显:Git使您可以工作。 Git内部的提交格式只有Git本身可以使用,但是您需要使用计算机来处理普通文件。 因此,Git可以从提交中填充工作树-尽管实际上它必须使用其索引(稍后我们将看到)-然后,您将拥有常规文件,常规程序,Web服务器或所有可以使用的常规文件。 由于许多原因,工作树还可以保存您不会提交的文件以及您从未打算提交的文件。 您将这些文件保留为“未跟踪”文件(Git会对它们发牢骚,并且您希望将其关闭)。

Git的索引 (也称为暂存区) (如git diff --staged )或有时称为缓存 (如git diff --cached :与--staged )具有多个角色,但有趣的是,它是您构建下一个提交的地方

当从某个现有存储库的克隆开始并签出某个分支名称时,Git将从快照中的索引中填充该分支上最新提交的索引。 (此最新提交称为尖端提交。)因此,现在索引与提交匹配。 因此,如果您现在要进行新的提交, 2您的新提交将具有与当前提交相同的树,因为您尚未更改索引。

当您运行git add来更新现有文件时,Git只是将索引副本替换为工作树中的版本。 现在,您进行的下一次提交将具有新版本。 当您运行git add以添加一个全新的文件时,Git将该文件从工作树复制到索引中,现在下一次提交将具有新文件。 如果在文件名上使用git rm ,则会从工作树和索引中删除该文件,现在下一次提交将没有该文件。

(顺便说一句,这使我们能够精确地说出这是什么意思了工作树文件是“未跟踪”。文件是未跟踪 ,当且仅当它是不是在索引中。这就是它-这一切就是这么简单!现在,当Git对未跟踪的文件发牢骚时,可以将文件名添加到名为.gitignore的文件中,这将使Git 关闭它。实际上不会使该文件处于未跟踪状态:这取决于文件是否未被跟踪。它只是使Git关闭,并且在您使用Git的“一次添加多个文件”快捷方式之一时也不会自动添加它。)


2 Git将尝试阻止您进行与HEAD提交完全匹配的新提交。 但是,您可以使用--allow-empty强制其允许提交。 “ Empty”是一种有趣的拼写方式,因为新提交根本不为空,至少在保存的工作树方面完全相同 (即使它们与HEAD匹配,也始终允许新的合并提交。)


git checkout何时覆盖索引和/或工作树?

如果我们回到我所谓的git checkout的第三种形式和第四种形式,我们会看到其中一种具有commit-ish参数,而另一种则没有。 这可能应该是更多隐藏的实现细节,但是Git习惯于让实现细节直接显示给用户。

为了git checkout将文件从提交复制到工作树,它必须首先将文件写入索引。 因此,第三种形式git checkout commit-ish -- path ,找到与给定commit-ish关联的文件path的版本,将其复制到索引,然后将索引版本复制到工作树。

但是,第四种形式没有commit-ish参数: git checkout -- path 在这种情况下,Git将文件的版本从索引复制到工作树中。 在大多数情况下,索引上版本与工作树中的版本相同 ,因此在大多数情况下,这没有任何作用。 但是,如果您已经修改了工作树版本,然后又决定要放弃所做的修改,则可以提取索引版本。

索引版本可能与当前提交( HEAD )版本相同。 在这种情况下, git checkout -- pathgit checkout HEAD -- path都将HEAD版本复制到工作树中,但是具有显式HEAD副本首先将HEAD版本复制到索引中,结果是相同的因为HEAD和索引版本仍然相同。

为了完整起见,我将提到前两个git checkout形式(“安全”形式) 将覆盖索引和工作树,但是在此不做过多详细说明,Git尝试仅覆盖那些是“安全”的覆盖,因此您不会丢失未提交的工作。 请参阅Git-在当前分支上有未提交的更改时签出另一个分支 (更多)。

摘要

每个文件始终有多达三个有趣的版本:

  • 当前( HEAD )提交中的一个;
  • 索引中的那个,将进入下一个提交;
  • 工作树中的那个。

使用git checkout commit-ish -- path ,如git checkout xxx . ,使HEAD版本保持不变,但将xxx (提交)版本复制到索引和工作树中。 如果索引和工作树中还有其他版本,那么它们现在就消失了。 如果这些版本与某个提交的版本匹配,则可以将其取回。 如果没有,Git帮不了您……可能。 但是请看最后一节!

特殊的秘密恢复方法

“永远消失”规则有一个不寻常的例外,尽管使用起来很痛苦。 当您在索引中git add文件时,Git实际上将文件的副本放入存储库中 索引仅包含文件的40个字符的SHA-1 哈希 这意味着,如果您在git add -ed文件中git add了该文件,则该文件将保存在存储库中。 如果用另一个版本覆盖索引版本,则实际上会将“另一个”版本复制到存储库中,并将新的哈希值放入索引中。 中间版本不会被删除! 好吧, 还没有

这些散列但从未提交的文件可以恢复,直到Git“垃圾收集”它们为止。 默认情况下,从git add -ed到它们至少有14天的时间。 恢复它们的命令是git fsck --lost-found

这种恢复方法的问题在于文件不见了。 git fsck --lost-found所做的是找到Git对象(提交,树,标签和“ blob”,Git称为存储的文件),这些对象没有引用它们。 当您对文件进行git add -ed时,Git将文件内容存储为新的blob,然后将blob对象的哈希ID写入索引,并使用索引来保存文件 然后,当您重写path的索引条目时,您会丢失名称,并且存储库blob对象变为未引用 --lost-found选项使git fsck将原始文件的内容复制到.git/lost-found/other/ ,并将其存储在哈希ID下,因为名称消失了。 然后,您可以浏览每个此类文件以找到所需的文件,并将其移出失物招领处以将其取回。

如果您在未预先存放或提交代码的情况下签出分支,则代码将丢失并且无法检索。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM