为什么 git 状态和 git 显示不一致?

Why do git status and git show disagree?

为什么 git statusgit 显示 不一致?

存储库应该位于标签中。这就是 git status 告诉我的(散列从 609b 开始)。

但是 git show 告诉我它在提交时哈希开始 156f

目前的情况是通过执行以下操作来获取代码库中的代码。

git clean -f && git reset HEAD --hard && git fetch && \ 
git fetch --tags && git checkout daily-build-492 && git pull

为什么不一致?

一些诊断:

$ git status
HEAD detached from daily-build-492
nothing to commit, working directory clean
$ git rev-list -n 1 daily-build-492
609b538fb0180b170170be09312fecf5a5240b6a
$ git show
commit 156f9e6b3fbfe7c16e8d821efd315428610043c2
Merge: ec154d9 15e8876
Author: ubuntu <ubuntu>
Date:   Wed Dec 2 14:09:23 2015 +0000
    Merge branch 'deploy-server'
$ git describe --tags --exact-match
fatal: no tag exactly matches '156f9e6b3fbfe7c16e8d821efd315428610043c2'
$ git log --tags --simplify-by-decoration --pretty="format:%ai %d" |grep 492
2015-11-23 07:05:18 +0000  (tag: daily-build-492)

我认为您同时被两个问题搞糊涂了。首先,您处于分离的 HEAD 状态。 Daily-build-492 是一个标签,您不能对标签进行更改。如果你在 master 中签出标签(例如)git 将首先给你这样的消息:

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

这基本上就是您在执行 git checkout daily-build-492 时可能所做的。如果您现在 git 状态,您将收到一条消息,内容如下:

$ git status
HEAD detached at daily-build-492

之后,您连接(使用 &&)一个拉命令,该命令从链接的远程获取更改。我的猜测是,因为这会添加对标签的更改并且因为这是不允许的,所以您会自动从 daily-build-492 分离到进一步的边缘 :) 现在通知将是:

$ git status
HEAD detached from daily-build-492
nothing to commit, working directory clean

如您所见,这正是您的 git 状态在您执行初始命令后实际告诉您的内容。

Git 状态只显示索引文件和 current HEAD 之间的所有差异。这基本上意味着与 HEAD 相比,您所做的所有更改都是全新的文件、更改或阶段性更改。基本上你 git 状态告诉你没有变化,你已经脱离了 HEAD。

Git显示

git show 做了一些不同的事情。 Git show 显示对象的信息。 git 中的对象可以是很多东西;一个提交,一个标签,一棵树等。如果你不给 git show 一个对象散列行为似乎是 git 显示最后一次提交。这是您在签出标签后拉入的提交。让您进入分离 HEAD 模式的提交之一。

简单复现你的问题

git init test.git
cd test.git
touch A
git add A
git commit -m "A, jay"
touch B
git add B
git commit -m "B, jay"
git tag TAG-1
git checkout TAG-1 //Now you will get the notice about git detached HEAD etc
git status // result below
//HEAD detached at TAG-1
//nothing to commit, working directory clean
touch C
git add C
git commit -m "C, shoopdawhoop" // a warning like below is outputted,but it works
//[detached HEAD eda8080] C
// 1 file changed, 0 insertions(+), 0 deletions(-)
// create mode 100644 C
git status // output below again
// HEAD detached from TAG-1
// nothing to commit, working directory clean
git show // will now tell us about commit C the last one we added.
// commit eda808088594ae7b05ae1b57ffd95f7f810a9091
// Author: a@example.net <a@example.net>

这一小部分命令基本上模仿了我认为您所做的。也许命令的 && 串联使输出静音,也许没有但你没有 post。这个故事的寓意是你试图做一些 git 不允许的事情。

正如您在示例中看到的那样,这解释了为什么您的 git describe --tags 没有产生任何结果,因为您给它提供了提交 C 的哈希值(不是真的,但它类似于我示例中的提交 C。 )

你的 git rev-list 确实给了你所期望的,因为你在 daily-build-492 的最后一次提交中特别要求它。

这个和 有两个键,但让我看看是否可以缩短它。 (这对我来说是一个挑战,因为我倾向于写很长的答案。:-))无论如何,我会尝试更多地确定一些事情。

首先,让我在这里补充一点,git pull 只是一个方便的脚本,它首先为您提供 运行s git fetch,然后——取决于您的配置和附加标志——运行s git mergegit rebase。 (默认是进行合并,这对很多用户来说通常是错误的。)

(你的问题也缺少一些信息。单独使用 git pull 应该会产生错误。但是,如果你使用 git pull origin master 或类似的,你应该会看到一些额外的输出从 git pull 执行第二步时开始。无论如何,我们可以看到您的最终状态是 HEAD 指向合并提交,尽管它的父项似乎都不是您的标记的提交标识。)

当你给它一个标签时,1 git checkout 会让你进入 "detached HEAD" 状态。这听起来有点可怕,就像某种 18 世纪的法国大革命,但实际上只是意味着你不再在任何分支上。 Git 可以在这种状态下工作:您可以进行新的提交,包括合并;他们就在 no 分支机构。找到它们的唯一方法是通过原始 SHA-1,例如 156f9e6b3fbfe7c16e8d821efd315428610043c2,或者通过一些从 HEAD 开始的相对名称,例如 HEAD 本身,HEAD~1HEAD~2,依此类推。2

通过签出标签进入 "detached HEAD" 状态,然后您至少再提交一次(实际上,根据 git show 输出至少再提交两次)。与分离的 HEAD 一样,这些新提交根本不在任何分支上。最后一个是合并,大概来自 git pull.

git merge 后半部分

现在 git status 显示您的 HEAD 所在的位置:分离,不是 at 标签,而是 from 标签。这意味着您之前就在标签处(处于分离的 HEAD 状态)。如果你当时有 运行 git status,你会看到这个:

$ git status
HEAD detached at daily-build-492

做出一些新的提交后,您转到 "detached from" 而不是 "detached at"。

另一方面,git show命令,如,显示一个对象。如果您自己不命名,默认显示的对象是任何 HEAD 名称的对象。在这种情况下,HEAD 命名了一个合并提交,因此 show 显示它的 ID、它的父 ID (Merge: ec154d9 15e8876)、作者和日期、提交消息(看起来像git pull 提供给它的 git merge 步骤)和组合差异(在本例中为空)。

有点奇怪的是,父提交都没有匹配标记提交的明显 ID,609b538fb0180b170170be09312fecf5a5240b6a。 (我在这里说 "apparent" 是因为你使用了 git rev-list -n 1 而不是 git rev-parse daily-build-492^{commit},并且有可能,但不太可能,在对要列出的提交进行排序之后,标签实际指向的是在 rev-list 输出中进一步向下。)而且,正如我之前提到的,git pull 没有额外的参数应该只会给你一条错误消息。

(我想我在 "shorter" 失败了 :-) 但也许至少这有助于弄清楚你是如何进入这种状态的。)


1或者实际上任何不是分支短名称的提交标识符。例如,git checkout refs/heads/master 使您获得与 git checkout master 相同的 commit,但使您处于 "detached HEAD" 状态。 (如果那是你想要的,你也可以使用 git checkout --detach master,我认为这更明显。)

2如果你 git checkout 其他东西使得 HEAD 现在指的是其他提交,你只剩下原始 SHA-1 ,或与某些修饰符相同。例如 156f9e6~1 指的是提交 156f9e6 的第一个父级,而 156f9e6~2 指的是该提交的第一个父级,依此类推。