Git:去了旧约,现在我不能回来了

时间:2017-01-13 17:17:00

标签: git

我使用以下内容移动到旧的提交,其中包含我删除的一些代码:

git checkout xxx .

但现在我想回到我工作的地方,我尝试了以下方法:我没有藏匿或提交我的最新代码

git checkout xxx/xxxx .

xxx / xxx是我一直在处理的分支的名称。

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

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:2)

我害怕par's answer可能是正确的人。{}但是,如果您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分支名称​​而没有路径参数,尝试将放在分支上。例如,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或{{1而不是使用--force-f选项。如果你有一个名为--force的文件,你需要能够告诉Git你所指的是名为master文件,而不是名为master的分支。 。如果省略master,Git会最好地猜测您是指分支名称,提交ID,选项还是文件名。在您的情况下,您使用--,这绝对是文件名,因此.成为第三种形式。当然,git checkout xxx .表示"整个当前目录,包括其所有文件和子目录"。

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

提交,索引和工作树

提交是Git存在的理由。他们记录你曾经做过的所有事情 - 或者至少是承诺 - 所有这一切,以便你可以得到这些。每个提交都包含一组文件的完整快照,排列在树中(目录中包含文件和子目录等)。它还包含先前(父)提交的ID,以及一些元数据,如您的姓名和电子邮件地址以及时间戳和提交日志消息。而这几乎就是它:提交是一个保存的树加上一些元数据。

工作树也是非常明显的:它是Git让你工作的地方。 Git中的提交是一种只有Git本身可以使用的格式,但你需要使用计算机来完成真正的工作,使用普通文件。所以Git可以从提交中填充工作树 - 但实际上它必须使用它的索引,我们稍后会看到 - 然后你有常规文件,常规程序,Web服务器或其他任何东西都可以使用。出于多种原因,工作树还可以保存您赢得提交的文件,并且您永远不打算提交。你会保留这些文件,因为"未跟踪"文件(和Git会对它们发牢骚,你会想要把它关闭)。

Git的索引,也称为临时区域(如git checkout --theirs)或有时缓存(如同git diff --staged:与git diff --cached完全相同),有几个角色,但有趣的是,它是你构建下一次提交的地方。 / p>

当你开始使用某个现有存储库的克隆并检查一些分支名称时,Git会从该分支上最新提交的快照中填充索引。 (这个最新的提交称为 tip 提交。)所以现在索引与提交匹配。因此,如果您现在要进行新的提交, 2 您的新提交将与您当前的提交具有相同的树,因为您还没有更改索引。

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

(顺便说一句,这使我们可以准确地说明工作树文件对于未经跟踪的#34;文件未跟踪的含义,当且仅当它不是在索引中。这就是它的全部内容!现在,当Git对未跟踪的文件感到愤怒时,您可以将文件的名称添加到名为{{1的文件中这将使Git 关闭它。它实际上不会使文件未被跟踪:这是由文件不在索引中确定的。它主要只是让Git闭嘴,当你使用Git"一次添加多个文件"快捷方式时,也不会自动添加它。)

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

--allow-empty何时覆盖索引和/或工作树?

如果我们回到我称之为HEAD的第三和第四种形式,我们会看到其中一个有 git checkout 参数和其他没有。这可能会成为一个隐藏的实现细节 - 但Git习惯让实现细节直接显示给用户。

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

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

索引版本可能与当前提交(commit-ish)版本相同。在这种情况下,git checkout -- pathHEAD都会将git checkout -- path版本复制到工作树中,但具有明确git checkout HEAD -- path的版本会将HEAD版本复制到索引首先,结果只是相同,因为HEAD和索引版本无论如何都是相同的。

为了完整起见,我会提到前两个HEAD形式 - " safe" ones-will 也会覆盖索引和工作树,但是在这里没有详细介绍,Git会努力只覆盖那些" safe"覆盖,以便你不会失去未提交的工作。有关(更多)详细信息,请参阅Git - checkout another branch when there are uncommitted changes on the current branch

摘要

每个文件最多有三个有趣的版本:

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

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

特殊秘密恢复方法

有一个不寻常的例外,#34;永远消失了#34;规则虽然使用起来很痛苦。当您HEAD索引文件时,Git实际上会将该文件的副本放在存储库中。索引仅包含文件的40个字符的SHA-1 哈希。这意味着,如果您xxx编辑了一个文件,它就会被保存在存储库本身中。如果你用另一个版本覆盖索引版本,那实际上会复制"而另一个版本#34;将版本放入存储库,并将新哈希放入索引中。中间版本没有被删除!好吧,尚未

这些哈希但从未提交的文件可以被恢复,直到Git"垃圾收集"他们。默认情况下,从您git add开始编辑它们至少需要14天。将恢复它们的命令是git add

此恢复方法的问题是文件的名称已消失。 git add所做的是找到Git对象 - 提交,树,标签和" blob",这就是Git调用存储文件的内容 - no 引用它们。当你git fsck --lost-found - 编辑文件时,Git将文件的内容存储为新的blob,然后使用索引保存blob对象的哈希ID 到索引中文件的名称。当您覆盖 git fsck --lost-found 的索引条目时,您丢失了名称,并且存储库blob对象变为未引用git add选项使path将原始文件的内容复制到--lost-found,并将其存储在哈希ID下,因为名称已消失。然后,您可以查看每个此类文件以找到所需的文件,并将它们移出丢失的区域以使其恢复。

答案 1 :(得分:0)

如果您在未事先隐藏或提交代码的情况下签出分支机构,您的代码将会丢失并且无法恢复。