社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Git

签出以前的提交后,无法看到新的git提交

Pratyanch Gor • 4 年前 • 679 次点击  

我做了一个git提交,vcs,然后我在前面的日志中签出了另一个。我找不到新的git提交到签出。 我丢失了所有更改,无法返回到最新提交

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/43427
 
679 次点击  
文章 [ 1 ]  |  最新文章 4 年前
torek
Reply   •   1 楼
torek    5 年前

DR

只要你在一个分支上做了承诺,你只需要使用 git checkout branch 现在。

如果你承诺 分离头 模式下,必须找到它的哈希ID。之后给哈希ID一个人类可读的名称(可能是分支名称)是明智的。你可以使用 git reflog 帮你找到丢失的散列ID。

注意,我在这里给你的问题加了几句话。这种特异性很重要。

…然后我从日志中签出了另一个更早的[commit],即使用 git log 我跑 git checkout a123456... 或类似的)。我找不到要签出的新git commit['s hash id]。我丢失了所有更改,无法返回到最新提交

如您现在所知,git散列id是git用来查找每个提交的唯一名称。也就是说,每个git提交都有一些难看的字母和数字字符串,git使用它们来定位该提交:

git checkout 5d826e972970a784bd7a7bdf587512510097b8c7

例如,查找提交(假设它存在于Git存储库中的Git的一个提交,因此它可能不在您自己的存储库中),并将其内容提取到一个我们可以使用的表单中。

您还知道,每个提交都包含源代码的完整快照。每个提交也有一些 元数据 ,有关提交的数据,如您的姓名(或作出该提交的人的姓名)、电子邮件地址等。您可能不知道的是,每个提交还存储其 起源 (前一个或前一个)提交。例如,我在上面使用的哈希ID为其哈希ID以开头的提交的父级 b5796d9a .

当你奔跑 Git日志 ,git的默认设置是首先显示 现在的 交给你已经结帐的人。然后git使用该提交保存的父id,以便向您显示该提交之前的提交。那个父母有另一个父母,所以在给你看了父母之后,git会转到祖父母那里,依此类推。所以你看不到 最新的 承诺:你从一个旧的承诺开始,然后向后移动。

这是使用git的第一个关键: 吉特倒着工作。 它必须从末尾开始,并朝着开头移动。但它实际上是从你选择的任何时间点开始的,所以要回到“现在”,你需要做一些不同的事情。

注意git会记住哪个commit是 现在的 使用git调用的机制提交 HEAD (用这样的大写字母拼写),尽管你可以使用符号。 @ 如果你愿意的话)。下面,当我们继续提到 ,记住这是Git的记忆方式 你现在的承诺 .

分支和标记名称

正如您刚才看到的,hash id对人类不是很友好。因此,我们通常不太使用它们,至少不直接使用,git也不使用 我们用它们。相反,git提供了将人类可读的名称链接到各种提交的能力。最重要的名字是 分支名称 ,但从说明 标签名 相反,因为分支名称有几个特殊属性。

标签名 只是提交的可读名称。例如,在git的git存储库中,标记名 v2.20.0 标识维护人员指定的“git 2.20”提交。因此,这个名字 V2.20.0 和大丑八怪有同样的功能 5d826e9729... 事情。所以不是:

Git结帐5D826E972970A784BD7A7BDF587512510097B8C7

我能做到:

git checkout v2.20.0

最终结果完全相同:我最终签出了git版本2.20(确切地说,是2.20.0)。

如果我这样做,我会得到git所说的 分离头:

$ git checkout v2.20.0
Note: checking out 'v2.20.0'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at 5d826e9729 Git 2.20

GIT把所有这些都打印出来的原因是新近Git的人忘记了这会产生一个分离的头部,如果你忘记了,分离的头部可能会有点疼。

退出这个分离的头部模式,我可以 git checkout master 回到 master 分支机构:

$ git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.

现在一切又恢复理智了。

分支名称是特殊的

分支名称,如标记名称,标识一些特定的提交:

$ git rev-parse master
5d826e972970a784bd7a7bdf587512510097b8c7

这又是同一个散列ID。但是分支名称和任何 其他 命名提交的方式,比如 V2.20.0 5d826e972970a784bd7a7bdf587512510097b8c7 . 尤其是当你使用 git checkout 使用原始散列id或标记名,git将实际的提交散列id直接存储在 . 这就是我们刚才看到的这种分离的头部模式。

一旦你进入这种模式, 只有 git具有的用于查找当前散列id的位置是特殊名称 . 每次你使用 GIT校验 通过其他的提交,它覆盖了git所嵌入的hash id 早期的.而且,既然git工作了 向后的 ,很容易去 向后的 从这里开始很难前进!

但是当你付出 GIT校验 分支名称 喜欢 主人 ,git做了一些完全不同的事情。而不是建立它的 为了记住原始散列id,git将其设置为 记住 分支名称本身 :

$ cat .git/HEAD
ref: refs/heads/master

所以现在你 on branch master 作为 git status 把它放进去。git不会直接记住hash id,而是记住您在这个分支上的事实。同时 分支名称 记住提交哈希。所以 附加到名称,并且该名称具有哈希ID。

如果你现在 新的 commit,git在这一点上做的特别聪明。git像往常一样构建新提交,使用当前提交的hash id作为新提交的父级。然后,一旦新提交完成并为其分配了丑陋的hash id,git就会写入新提交的hash id 进入之内 这个 名称 主人 ,这是 附上。

我们可以把它画成图片,尽管它有助于用单个字母替换大的丑陋的哈希ID。假设我们正在进行哈希ID为的提交 H ,其父项具有哈希ID G ,其家长是 F ,等等。我们说每一个承诺 指向 它的父母:

... <-F <-G <-H

这个 分支名称 主人 持有 H 的哈希ID,所以 主人 它本身指向 H :

... <-F <-G <-H   <--master

最后, 附于 主人 ,以便如果有多个分支名称,git知道要使用哪一个:

...--F--G--H   <-- master (HEAD)
         \
          I--J--K   <-- develop

所以如果我们现在做出新的承诺,它将被分配一个新的哈希IDI将使用下一个字母, L ,这里–它指向 H ,然后git重写名称 主人 所以它指向 L :

...--F--G--H--L   <-- master (HEAD)
         \
          I--J--K   <-- develop

所以这是使用git的第二个关键: 分支名称移动。 分支名称包含 最后的 在分支上提交。制作一个 新的 承诺,当您有某个特定的分支签出时 附加到该分支名称 自动 移动分支名称。

自从你 附加到分支名称,分支名称现在标识 新的 最后提交, Git日志 将在提交时开始 L 向后看,给你看 L 然后 H 然后 G 等等。

这就是为什么分离的头有点痛

假设您有上面的原始序列,但是您使用 GIT校验 使用原始哈希ID选择历史提交 G . 为了使事情适合我们需要推动 H 有点:

          H   <-- master
         /
...--F--G   <-- HEAD
         \
          I--J--K   <-- develop

现在让我们做一个新的承诺 L :

          H   <-- master
         /
...--F--G--L   <-- HEAD
         \
          I--J--K   <-- develop

请注意 只有 新提交的名称 L . 现在让我们用 GIT校验 移动 我们的 或者重新连接。例如,我们把它重新连接到 主人 :

          H   <-- master (HEAD)
         /
...--F--G--L
         \
          I--J--K   <-- develop

这个 Git日志 命令将在提交时启动 H 向后看,给我们看 H 然后 G 然后 f ,依此类推。

如果我们还没有保存(写下来,或者在终端窗口中的scrollback中,或者其他什么)commit的hash id L ,我们将如何再次找到此提交?

这就是重新登录的地方

为了帮助我们摆脱错误,git存储了 以前的值 每一个不同的名字。也就是说,如果 主人 指向 G 在我们做出决定之前 H 然后我们 H , 主人 现在指向 H 但是 用于 指向 G ,因此git保留一个reflog条目,拼写为 master@{1} ,记住这一点 主人 曾经指向 G .

git对这个特殊的名字也有同样的作用 . 每次我们签出一些新的提交,git都会更新 重新漂浮。我们可以看到这种重演。 吉特雷弗洛格 ,它默认显示 :

$ git reflog
5d826e9729 (HEAD -> master, tag: v2.20.0, origin/master, origin/HEAD) HEAD@{0}: checkout: moving from 5d826e972970a784bd7a7bdf587512510097b8c7 to master
5d826e9729 (HEAD -> master, tag: v2.20.0, origin/master, origin/HEAD) HEAD@{1}: checkout: moving from master to v2.20.0

在本例中,前两个reflog条目并不特别有趣,因为这两个条目都是针对 主人 . 但是如果你做出这样的承诺 L 在上图中,现在需要找到它的散列ID, 吉特雷弗洛格 会很有帮助。

为现有提交提供新的分支名称

假设你确实 L 像这样,已经失去了它:

H<—主(头)
/
-----------L
\
发展

然后你使用 吉特雷弗洛格 找到它,你就找到了。你现在可以查出来:

git checkout llllllllllllllllllllllllllllllllllllllll

(当然,使用实际的hash id)。你现在将处于“独立的头脑”模式,现在是时候给你建议 GIT校验 当你第一次进入这个分离的头部模式时,它会自动打印。它说我们可以使用 git checkout -b :

git checkout -b xyz-branch

例如。

这是为了 创造 新的 分支名称,指向当前(即, )提交,然后立即附加 为了那份承诺。所以现在我们有:

          H   <-- master
         /
...--F--G--L   <-- xyz-branch (HEAD)
         \
          I--J--K   <-- develop

现在我们都准备好了,因为我们有一个 可读名称 (“XYZ分行”)代表 最后的 在这个新分支上提交。每次我们通过签出分支、做一些工作并提交来添加新的提交时,git都会自动更新新的分支名称。