嘿,git,最后一次提交发生了什么? [英] Hey git, what happened to the last commits?

查看:62
本文介绍了嘿,git,最后一次提交发生了什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是在提交后使用了 check out head .我认为进行此结帐实际上不会做任何事情,但是我似乎错了.它使我陷入脱节"状态.我忽略了此注释,并继续进行了一些其他提交.消息更改为头部从...分离",对此感到有些烦恼,我寻找了一种修复它的方法.我找到的答案是 git checkout master .我这样做了,现在我的最后几次提交都消失了.这里发生了什么事?

I just used check out head after making a commit. I figured that doing this checkout would not actually do anything, but I seem to have been wrong. It put me into a 'detached head' state. I ignored this note, and continued to make a few additional commits. The message change to 'Head detached from ...' Feeling a bit annoyed by this, I looked for a way to fix it. The answer I found was git checkout master. I did this and now my last few commits disappeared. What happened here?

推荐答案

TL; DR

使用 git reflog 查找丢失的提交.给他们一个名称(例如,分支名称),然后考虑是否使用 git cherry-pick git rebase 将它们复制到经过改进的新提交中.

TL;DR

Use git reflog to find the lost commits. Give them a name—e.g., a branch name—and then consider whether to copy them to new-and-improved commits, perhaps using git cherry-pick or git rebase.

我不清楚您为进入分离的头"而做了什么.状态.(通过支票名以外的任何标识符检出提交的任何操作都可以做到这一点.例如,如果您给 git checkout 分配了标签名,或者使用了远程-跟踪名称,例如 origin/master ,它将分离.同样,如果您通过其哈希ID检出特定的提交,则将处于该模式.即使在检查时也可以故意这样做使用-detach 通过分支名称提交,尽管我想不是那样.)

It's not clear to me what you did to get into "detached HEAD" state. (Anything that checks out a commit by any identifier other than a branch-name will do that. For instance, if you give git checkout a tag name, or a remote-trackng name like origin/master, it will detach. Likewise, if you check out a specific commit by its hash-ID, you'll be in that mode. You can also deliberately do it even when checking out a commit by branch-name, using --detach, although I imagine that was not it.)

注意: HEAD 至少当前被实现为 .git 目录中的纯文本文件.该文件通常包含分支的名称.完成后,Git会说您处于"状态该分支:例如, git status 会在分支主控上说 或在分支分支上说 .在分离式HEAD 模式下,文件中没有的名称(请参阅下文了解更多信息).在这种情况下, git status 会说 HEAD在...上分离 HEAD从...上分离的或类似名称(确切的措词取决于几个项目,包括您的特定Git版本).

Note: HEAD is, at least currently, implemented as a plain-text file inside the .git directory. This file normally contains the name of a branch. When it does, Git says that you are "on" that branch: git status will say on branch master or on branch branch, for instance. In detached HEAD mode, the file doesn't have a name in it (see below for more). In this case git status will say HEAD detached at ... or HEAD detached from ... or similar (the exact phrasing varies depending on several items, including your particular Git version).

这就是我喜欢绘制提交图的方式:

Here's how I like to draw commit graphs:

...--D--E--F   <-- master
      \
       G     <-- branch (HEAD)

这表示一个具有大量提交的 master 分支,以及一个对其进行了一次提交(commit G )的名为 branch 的分支不在 master 上.这里的 master 有两个不在 branch 上的提交(提交 E F ):分支名称 master 指向"提交 F (其中已提交 F 的哈希ID),提交 F 指向提交 E (通过列出 E 的原始哈希ID作为其父代),提交 E 指向 D ,依此类推.分支名称 branch 指向提交 G ,而 G 指向 D .(HEAD)部分说HEAD是 attached ,名称为 branch .

This represents a master branch with a bunch of commits, and a branch named branch with one commit on it (commit G) that is not on master. Here master has two commits that are not on branch (commits E and F): The branch name master "points to" commit F (has commit F's hash-ID in it), commit F points to commit E (by listing the raw hash ID for E as its parent), commit E points to D, and so on. The branch name branch points to commit G, and G points to D. The (HEAD) part says that HEAD is attached, to the name branch.

在这种状态下,如果您进行新的提交,则会像往常一样添加它.如果我们标记新的提交 H ,则提交 H 的父提交是提交 G ,而 branch 是更新为指向提交 H ,给出:

In this state, if you make a new commit, it's added as usual. If we label the new commit H, commit H's parent-commit is commit G, and branch is updated to point to commit H, giving:

...--D--E--F   <-- master
      \
       G--H   <-- branch (HEAD)

当您在此分离头"中时,另一方面, HEAD 不包含分支名称.相反,它包含标识当前提交"的原始哈希ID.假设您决定看看上面的commit E :

When you are in this "detached HEAD" mode, on the other hand, HEAD does not contain the name of a branch. Instead, it contains a raw hash-ID, identifying the "current commit". Let's say you decide to take a look at commit E above:

$ git checkout master^      # some windows users may have to write ^^

master ^ 标识提交 E ,并且不是分支名称,因为它里面有那个 ^ 字符,因此这可以帮助您提交 E (并且我必须使用另一行来提高 F ,以便我可以绘制 HEAD 的箭头):

master^ identifies commit E, and is not a branch name because it has that ^ character in it, so this gets you on to commit E (and I have to use another row to raise F so that I can draw in the arrow for HEAD):

          F   <-- master
         /
...--D--E     <-- HEAD
      \
       G--H   <-- branch

现在,如果您处于这种状态并添加新的提交,则它们将以与往常相同的方式添加-但是没有要更新的分支,因此Git将每个新提交的ID直接写到 HEAD .假设我们现在创建提交 I :

Now, if you are in this state and add new commits, they are added in the same way as always—but there is no branch to update, so Git writes each new commit's ID directly into HEAD. Let's say we now create commit I:

          F   <-- master
         /
...--D--E--I   <-- HEAD
      \
       G--H   <-- branch

提交 I 具有提交 E 作为其父级,并且 HEAD 现在指向提交 I .但是,如果您决定要再次查看分支:

Commit I has commit E as its parent, and HEAD now points to commit I. However, if you decide you want to go look at branch again:

$ git checkout branch

您现在将如何找到提交 I ?唯一的名称 HEAD ,在上述结帐之后,图片如下所示:

how will you now locate commit I? The only name it had was HEAD, and after the above checkout, the picture now looks like this:

          F   <-- master
         /
...--D--E--I
      \
       G--H   <-- branch (HEAD)

(注意:如果您在分离的HEAD"状态下进行了多次提交,则这些提交例如可能是 IJK .这不会改变您在下面需要执行的任何操作,除了进行更多工作以确保您拥有 last 最后的提交,即 K .我只使用一次提交 I

(Note: if you made more than one commit while in "detached HEAD" state, they might be commits I-J-K for instance. This doesn't change any of what you need to do below, except to make it a bit more work to be sure you have the last commit, K. I'll just use the one commit I below.)

没有标签可让您查找提交 I .您无法通过提交 E 来找到它: E 的父代仍然只是 D ,而 E not 列出其子级.您也不能从 F G H 提交中找到它;他们甚至都没有关系.提交 I 被放弃",大约一个月后它就会真正消失.

There's no label letting you find commit I. You can't find it from commit E: E's parent is still only D, and E does not list its children. You cannot find it from commits F, G, or H either; none of them are even related. Commit I is "abandoned", and in about a month it will truly go away.

但是,正如 Nikhil Gupta指出的一样, 仍然是一种查找提交的方法 I (直到大约一个月内收集到):它存储在git称为"reflog"的文件中.(或更准确地说,一个"引用日志:每个分支有一个引用日志,而 HEAD 则有一个大引用日志.)实际上,引用日志本身是保持提交 I 大约一个月.被遗弃的存储库对象仍然可以通过reflog引用进行命名,但是这些reflog条目将过期.一旦提交 I 的reflog条目到期,如果您尚未附加更永久的链接,则git的垃圾回收过程将真正删除它.

However, as Nikhil Gupta noted, there is still a way to find commit I (until it is collected in about a month): it's stored in what git calls the "reflog". (Or more precisely, "a" reflog: there's one reflog for each branch, plus one big one for HEAD.) In fact, it's the reflog itself that keeps commit I around for about-a-month. Abandoned repository objects are still name-able through reflog references, but those reflog entries expire. Once the reflog entry for commit I expires, if you have not attached a more-permanent link to it, git's garbage-collection process will truly delete it.

因此,要取回提交,请使用 git reflog 查看 HEAD reflog.这将显示 54ce513 HEAD @ {3}之类的内容:commit:foo the bar .您可以将 HEAD @ {3} 或缩写的哈希ID 54ce513 1 提供给各种git命令,包括 gitlog git show .

So, to get your commits back, use git reflog to view the HEAD reflog. This will show you things like 54ce513 HEAD@{3}: commit: foo the bar. You can supply either HEAD@{3} or the abbreviated hash-ID, 54ce513,1 to various git commands, including git log and git show.

一旦有了所需的哈希ID(或诸如 HEAD @ {3} 之类的名称),就可以在其上附加名称(标签或分支名称):

Once you have a desired hash-ID (or name like HEAD@{3}), you can attach a name—a tag or branch name—to it:

$ git tag get-it-back 54ce513  # or git tag oops HEAD@{3}

或:

$ git branch experiment 54ce513

,现在您可以使用名称 get-it-back experiment 引用提交 I .如果将其命名为分支名称,则现在有一个普通的普通分支:

and now you can refer to commit I with the name get-it-back or experiment. If you make it a branch name, you have a regular ordinary branch now:

          F   <-- master
         /
...--D--E--I   <-- experiment
      \
       G--H   <-- branch (HEAD)

一旦它有了一个方便的名称,您就可以用它来做任何想做的事情.或者,您也可以(在大约30天的时间里)仅通过reflog-name或raw hash-ID引用它.例如,您可以将提交 I 中的更改复制到分支 branch (其中 HEAD 仍指向)上的新提交中:

Once it has a convenient name, you can do whatever you want with it. Or you can (for the ~30 days it sticks around) just refer to it by reflog-name or raw hash-ID. For instance, you could copy the changes in commit I onto a new commit on branch branch (where HEAD is still pointing):

$ git cherry-pick 54ce513

( cherry-pick 基本上是指找出我在该提交中所做的,然后在当前分支上再次执行").假设您没有 not 附加名称以提交 I ,这将为您提供:

(cherry-pick basically means "find out what I did in that commit, and do it again on the current branch"). Assuming you did not attach a name to commit I, this would give you:

          F   <-- master
         /
...--D--E--I
      \
       G--H--J <-- branch (HEAD)

从提交 H 到提交 J 的差异与 E 2 >到 I .

where the difference in moving from commit H to commit J is the same2 as the diff from E to I.

1 字符串 HEAD @ {3} 是相对引用":" HEAD 3以前更改过的位置".像 54ce513 这样的哈希ID是绝对的":它永远不会改变,并且(与完整ID的其余部分一起缩写)是真实名称".的提交.由于每次您执行 commit checkout HEAD 都会更改,因此如果您执行 git checkout HEAD @ {3} ,它就变成了 HEAD @ {4} —尽管现在 HEAD @ {0} 现在包含了 54ce513 .如果您 git checkout 54ce513 始终有效,那么,当然,假设 54ce513 是实际的哈希ID(我做了这个特别的设置).

1The string HEAD@{3} is a "relative reference": "where was HEAD 3 changes ago". A hash-ID like 54ce513 is "absolute": it never changes, and is (with the rest of the full ID—this one is abbreviated) the "true name" of the commit. Since HEAD changes every time you do a commit or checkout, if you do git checkout HEAD@{3}, it becomes HEAD@{4}—although of course HEAD@{0} now also contains 54ce513. If you git checkout 54ce513, that always works—assuming, of course, that 54ce513 is the actual hash-ID (I made this particular one up).

2 更具体地说,这些提交之间的 git diff 显示相同的更改,但有一个例外:由于cherry-pick使用Git的合并机制,因此Git有时可以告诉您已经有一些更改,请避免重复.

2More specifically, git diff between those commits shows the same changes, with one exception: because cherry-pick uses Git's merge machinery, Git can sometimes tell that you already have some of the changes, and avoid duplicating them.

这篇关于嘿,git,最后一次提交发生了什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆