首页 > 解决方案 > Git tag 也标记所有以前的提交

问题描述

目前我正在尝试让我的库在 github 上可用。有两个版本:0.10.2。我知道pip可以通过它在 git 上的标记找到一个包版本,所以我考虑标记它。

当我做:

git add .
git commit -m "msg1"
git tag -a 0.1 -m "lib v0.1"
git push origin master --tag 0.1

第一次提交标记为 0.1

但是当我在代码中改变一些东西时,我做的几乎一样:

git add .
git commit -m "msg2"
git tag -a 0.2 -m "lib v0.2"
git push origin master --tag 0.2

最后一次提交标记为 0.2,但第一次提交标记为 0.2 和 0.1。难道我做错了什么?还是它应该是这样?

@编辑。这是它在我的 git 上的样子:

第一次提交: 在此处输入图像描述

第二次提交: 在此处输入图像描述

Releases标签:

在此处输入图像描述

标签: gitgithubgit-tag

解决方案


TL;博士

一切都好。

GitHub 试图向 GitHub 用户隐藏 Git 的许多复杂性。我相信这是一个错误。虽然众所周知,Git 对用户不友好(例如,请参阅“我讨厌 Git 的 10 件事”xkcd #1597),但其中一些复杂性确实是必要的。1

在 Git 标签的情况下,关于标签要意识到的一点是,任何一个标签都只是某个特定哈希 ID 的人类可读名称。哈希 ID 是那些大而丑陋的数字,GitHub 变灰和缩写,几乎无法阅读。在您的屏幕截图中,此处显示的两个是a89f1cc31a6a2d

这些哈希 ID 是 Git 对象的真实名称,即 Git 本身用来查找对象内容的名称。这些散列 ID 本身是通过对内容应用加密散列生成的,因此散列 ID 是键值数据库的短(ish)键:给定键,保证对于这些特定内容是唯一的,2 Git 可以查找该值并获取全部内容。由于两个哈希值不同,因此两个对象也必然不同。如果两个对象相同,则两个哈希值相同。

对于带注释的标签对象,实际上有必要再深入一点:带注释的标签的内容包括目标对象的哈希值。这最容易从命令行完成——同样,GitHub 隐藏了细节,并不是说命令行也很清楚:

git rev-parse a89f1cc^{}

例如会查找 object a89f1cc,检查它是否是一个带注释的标签对象,如果是,就跟随它到标签本身命名的任何其他对象。如果a89f1cc是其他类型的对象,则后缀^{}无效。我们也可以这样写:

git rev-parse a89f1cc^{commit}

它将找到解决或产生错误的提交对象,在这种特殊情况下,这将是我们想要的:如果它已经是一个提交,a89f1cc这将给我们它自己的完整哈希 ID ,或者产生哈希 ID解决a89f1cc提交,如果它a89f1cc解析为提交,或者错误输出。

对于使用系统的人来说更实际的是,给定两个标签名称,您可以只使用名称

git rev-parse v0.1^{commit}

和:

git rev-parse v0.2^{commit}

找到标签指向的提交。^(某些 shell 可能需要在帽子和/或大括号字符周围进行某种引用{...};这取决于您是否使用 bash、tcsh、PowerShell 或其他任何东西。您可能需要稍微调整这些命令行命令以使您的 shell 满意。 )

将两个标签哈希中的每一个解析为提交将告诉您哪个提交或提交了两个不同的标签名称。可以为单个提交提供多个标签,但您没有这样做,因此您将获得两个不同的提交哈希。这些提交pip install将检查、构建和安装。


1阿尔伯特爱因斯坦经常被解释为说一切都应该尽可能简单,但不是更简单,尽管Wikiquote 说他实际上写了几乎不能否认所有理论的最高目标是使不可约的基本元素尽可能简单尽可能少,而不必放弃对单一经验数据的充分表示。

2这有点夸大其词,具体取决于您的 Git 版本。有关详细信息,请参阅git 中的哈希冲突


如果您是 Git 用户,还需要了解更多信息:标签与分支以及提交图

如果您只是要安装,那么以上内容可能就是您所关心的。然而,作为一个使用 Git 本身的人,还有另一件事要学习。

我们在上面看到标签名称通过git rev-parse(大多数 Git 命令在适当时在内部执行)解析为哈希 ID。但是对于像or之类的分支名称也是如此——嗯,大部分是真的。我们可以运行:masterdevelop

$ git rev-parse v2.16.0
e1c2c6b098dfb717a4a6ff7f3894d57343210a41

并获得这样的哈希 ID,但我们也可以运行:

$ git rev-parse master
ccdcbd54c4475c2238b310f7113ab3075b5abc9c

并获得一个哈希 ID。此哈希 ID 是名称master指向的(一个,单个!)提交。这适用于Git的所有引用:分支名称、标签名称、远程跟踪名称origin/master

分支名称的特别之处在于它们随着时间而变化。它们现在存储一个哈希 ID,然后在你运行之后git commit,一些分支名称存储一个新的、不同的哈希 ID。这实际上是分支的增长方式:你告诉 Git 你想某个分支上,通过运行,例如:

git checkout master

之后git statuson branch master位于分支上的意思是,git commit它将为您更改该分支名称:Git 将进行的提交,该提交将获得一些新的、看起来随机的哈希 ID,然后 Git 会将提交的哈希 ID 存储到名称中master.

其底层机制是特殊名称HEAD(全部大写,尽管在 Windows 和 Mac 系统上进行大小写折叠,您通常可以使用全小写)。Git将单词附加HEAD到分支名称上,以便记住您要求 Git 在哪个分支上。

如果我们为分支名称使用单个大写字母,我们可以这样画出:

A <-B <-C   <-- master (HEAD)

这意味着它HEAD附加到master,并且名称master 指向提交C- 它包含提交的哈希 ID C。提交C本身包含先前提交的哈希 ID B,而提交B包含提交的哈希 ID A

在我们的例子中,我们的存储库很小:它只有这三个提交。提交A没有更早的提交——Git 称之为根提交——所以它没有指向任何地方。这显示了 Git 是如何工作的:它使用名称 master来查找当前提交的哈希 ID C,然后使用提交的内容来C查找提交。let Git find的 parent的内容,并且没有 parent 所以现在动作停止:Git 显示你 commit , then , then ,然后停止。CBBBAACBA

换句话说,Git 是向后工作的。

当您进行新的提交D时,Git 通过记录所有代码的快照、记录先前提交的实际哈希 ID C、然后写出提交并获取其实际哈希 ID(无论是什么)来创建此提交。然后 Git 使用HEADto know 将新的哈希 ID 写入master,给出:

A <-B <-C <-D   <-- master (HEAD)

现在master命名(指向) commit D

同样,这就是树枝通常移动和生长的方式。分支名称 master标签名称 之间的区别在于,分支v0.1名称应该移动——应该随着时间的推移而改变,以命名该分支的最后一次提交——但标签名称不会移动。一旦你分配了一个标签名来指向某个特定的提交,它就会粘在那里:它一直指向那个提交。

从提交到提交的链接形成了一个,正是这个提交图真正将 Git 粘合在一起。GitHub 试图对你隐藏提交图。因为 Git 主要是关于图形的——仅仅是为了顺风顺水的文件——这是一个可怕的损害。理解提交图对于使用 Git 至关重要。


推荐阅读