MYZ*_*MYZ 6 git visual-studio-code gitlens
现在的情况非常可怕:我已经使用了扩展GitLens来VSCode跳回旧的提交。我想要checkout提交,将其放在COMMITS侧边栏中,右键单击并选择Switch to Commit...。我确实希望签出该提交,然后能够签出回到我当前的状态。
现在运行git log显示我的提交日志,仅显示到我选择的提交点。这太可怕了。我的新提交在哪里?
现在我无法找到我的新提交并返回它们。我在切换到旧的提交之前做了一个新的提交,所以我 100% 确定应该有更新的提交。这是一个新项目,我尚未提交到任何远程位置,因此git pull无法保释我。
我真的希望有人能帮助我,我不想失去两天的工作......
tor*_*rek 11
对于 Git 新手来说,这很可怕。但不用担心:所有提交仍然存在。
\n各种 GUI,包括 Visual Studio,都会阻止对 Git 的访问(这可能是好是坏,取决于您的观点),因此您无法看到到底发生了什么,而且我不使用这些GUI,因为它们使您无法看到正在发生的事情,所以我无法准确地说出 GUI 中每个可点击按钮的作用。 然而,Git 的工作原理如下:
\n始终有1 个当前提交。Git 对此提交有一个特殊的名称:HEAD,全部大写,就像这样。2
大多数时候,还有一个当前分支。Git 有一个特殊的名称,您可以通过它访问当前分支:HEAD。
你可能\xe2\x80\x94事实上,你应该\xe2\x80\x94在这一点上反对:我们如何知道是HEAD指提交还是指分支名称? Git 的答案是:我根据目前想要的选择其中之一。 有些东西需要分支名称,在这种情况下,HEAD就变成分支名称。有些事情需要提交,在这种情况下就HEAD变成了提交。基本上,Git 有两种内部方式来询问HEAD now 是什么。一个给出分支名称答案,例如master或main或其他什么,另一个给出原始提交哈希 ID。
好的,考虑到这一点,我们现在记得git log打印出这样的日志:
commit eb27b338a3e71c7c4079fbac8aeae3f8fbb5c687 (...)\nAuthor: ...\n ...\n\ncommit fe3fec53a63a1c186452f61b0e55ac2837bf18a1\n...\nRun Code Online (Sandbox Code Playgroud)\n也就是说,我们看到所有这些奇怪的哈希 ID 一次一个地溢出。哈希 ID 是每个提交的真实姓名。每个提交都会获得一个全局唯一的哈希 ID:不允许两个不同的提交拥有相同的哈希 ID。这就是哈希 ID 如此大且难看的原因。它们看起来很随意。它们实际上不是随机的,但它们是不可预测的。3
\n分支名称(例如main)可转换为提交哈希 ID。原始哈希 ID 已经是哈希 ID。无论哪种方式,只要给出正确的哈希 ID,Git 就可以找到该提交。
每个提交都包含每个文件的完整快照,4加上一些元数据:有关提交本身的信息,例如谁进行的、何时进行的,以及他们当时可以写入的日志消息。对于 Git 本身来说至关重要的是,此元数据中的一项是上一次提交的原始哈希 ID。
\n这里还有一个关于提交的随机事实值得记住:一旦提交,任何提交的任何部分都不能更改。这就是哈希 ID 的实际工作方式,这对于 Git 作为分布式版本控制系统至关重要。但这也意味着任何 Git 提交都不能包含其未来子提交的原始哈希 ID ,因为我们不知道创建提交时它们会是什么。提交可以存储其父母的“名称”(哈希 ID),因为当我们创建孩子时,我们确实知道他们的祖先。
\n这对我们来说意味着提交会记住它们的父母,这形成了一种向后看的链条。我们所要做的就是记住最新提交的原始哈希 ID。当我们这样做时,我们最终会得到一条链,我们可以这样画:
\n... <-F <-G <-H <--main\nRun Code Online (Sandbox Code Playgroud)\n在这里,名称保存了 最新提交main的真实哈希 ID ,出于绘图目的,我们将其称为。Commit依次保存较早提交的哈希 ID ,后者保存更早提交的哈希 ID ,依此类推。HHGF
我们现在可以看到如何git log工作:它从当前分支所选择的当前提交,开始。为了成为当前分支,我们将特殊名称附加到名称上:Hmainmain HEADmain
...--F--G--H <-- main (HEAD)\nRun Code Online (Sandbox Code Playgroud)\nGit 使用HEAD查找main,使用main查找H,并向我们展示H。然后 Git 使用H查找G并向我们展示G;然后它使用Gfind F,等等。
当我们想要查看任何历史提交时,我们通过哈希 ID 将其挑选出来,并告诉 Git:直接附加HEAD到该提交。我们可以这样画:
...--F <-- HEAD\n \\\n G--H <-- main\nRun Code Online (Sandbox Code Playgroud)\n当我们git log现在运行时,Git 会翻译HEAD成这次直接找到的哈希 ID\xe2\x80\x94;没有附加的分支名称\xe2\x80\x94 并向我们显示 commit F。然后从那里向后git log移动。提交和在哪里?他们无处可见!GH
但这没关系:如果我们运行git log main,git log则以 name 开头main,而不是 name HEAD。找到 commit H,git log显示;然后git log移动到G,依此类推。或者,我们甚至可以运行:
git log --branches\nRun Code Online (Sandbox Code Playgroud)\n或者:
\ngit log --all\nRun Code Online (Sandbox Code Playgroud)\n找到所有分支或所有引用git log(“引用”包括分支和标签,但也包括其他类型的名称)。
(这带来了另一个单独的蠕虫罐头,它是关于如何git log处理“想要”“同时”显示多个提交的情况。我根本不会去那里,在这个回答。)
这种“查看历史提交”模式,在 Git 中被称为分离 HEAD 模式。这是因为特殊名称HEAD不再附加到分支名称。要重新附加您的HEAD,您只需选择一个分支名称,使用git checkout或 (Git 2.23 或更高版本)git switch:
git switch main\nRun Code Online (Sandbox Code Playgroud)\n例如。您现在已经签出了分支名称选择的提交main,并且HEAD现在重新附加到名称main。
在我们停止之前,还有一件非常重要的事情需要学习,那就是:树枝如何生长。但让我先把脚注去掉。
\n1此规则有一个例外,这在一个完全没有提交的新的、完全空的存储库中是必需的。稍后可以在非空存储库中以奇怪的方式使用该异常。但你不会使用这个。
\n2小写变体 ,head通常在 Windows 和 macOS 上“有效”(但在 Linux 和其他系统上无效)。然而,这是具有欺骗性的,因为如果您开始使用该git worktree功能,head(小写)将无法正常工作\xe2\x80\x94,有时它会让您得到错误的提交!\xe2\x80\x94,而HEAD(大写)却可以。如果您不喜欢全部大写,请考虑使用简写@字符,您可以使用它来代替HEAD。
3 Git 在这里使用加密哈希:与加密货币中的内容相同,尽管没有那么严格(Git 目前仍然使用 SHA-1,它在加密术语中已经过时了)。
\n4快照以特殊、只读、仅限 Git、压缩和重复数据删除的格式存储。Git将提交显示为“自上次提交以来的更改”,但将提交存储为快照。
\n假设我们有以下情况:
\n...--G--H <-- main (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n我们现在想要进行新的提交,但我们想将其放在新的分支上。因此,我们首先为 Git 创建一个新的分支名称,并将该名称H也指向提交:
git branch develop\nRun Code Online (Sandbox Code Playgroud)\n结果是:
\n...--G--H <-- develop, main (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n现在我们选择带有or 的develop名称作为HEAD附加名称:git checkoutgit switch
...--G--H <-- develop (HEAD), main\nRun Code Online (Sandbox Code Playgroud)\n请注意,我们仍在使用commit H。我们现在只是通过其他名称使用它。直到并包含的提交H都在两个分支上。
现在,我们按照 Git 中通常的方式进行新的提交。准备就绪后,我们运行git commit并向 Git 提供一条日志消息,以放入新提交的元数据。现在 Git:
I\xe2\x80\x94)将向后指向现有提交H;user.nameanduser.email作为此新提交的作者和提交者,使用“now”作为日期和时间;H,部分来自我们保存的快照:新提交中的所有内容都会构成新的随机数-看起来哈希 ID,这就是我们无法预测它的原因。)现在我们有了这个新的提交I,它指向现有的提交H:
...--G--H\n \\\n I\nRun Code Online (Sandbox Code Playgroud)\n现在,Git 执行了另一个让一切正常工作的魔法:git commit将I\ 的哈希 ID 写入当前分支名称。也就是说,Git 用于HEAD查找当前分支的名称,并更新存储在该分支名称中的哈希 ID。所以我们的图片现在是:
...--G--H <-- main\n \\\n I <-- develop (HEAD)\nRun Code Online (Sandbox Code Playgroud)\n该名称HEAD仍然附加到分支名称develop,但分支名称develop现在选择 commit I,而不是 commit H。
正是 commitI导致返回 commit H。该名称只是让我们找到提交。提交才是真正重要的:分支名称只是让我们找到最后一次提交。无论该分支名称中的哈希 ID 是什么,Git 都会表示该提交是该分支上的最后一次提交。所以既然现在main说,是最后一次提交;因为现在说,是最后一次提交。向上提交仍然在两个分支上,但仅在.HHmaindevelopIIdevelopHIdevelop
稍后,如果我们愿意,我们可以让 Git移动名称main。一旦我们移动main到I:
...--G--H--I <-- develop, main\nRun Code Online (Sandbox Code Playgroud)\n然后所有提交都会再次出现在两个分支上。HEAD(我这次省略了,因为如果都选择 ,我们可能不关心我们处于“哪个分支” I。事实上,我们可以删除其中一个名称\xe2\x80\x94,但不能删除两个\xe2\x80\x94,因为两个名称都选择相同的提交,这就是我们找到正确的哈希 ID所需的全部内容。如果我们要将这个哈希 ID 写在某个地方,我们可能不需要任何名称。但这充其量只是......令人讨厌。我们有一台计算机;让它以漂亮整洁的名称为我们保存又大又难看的哈希 ID。)
| 归档时间: |
|
| 查看次数: |
2540 次 |
| 最近记录: |