里程碑就是Git中的tag,tag是与某个具体的提交(commit)关联的,使用里程碑的好处在于可以直观的看到版本的演变历史,而不是简单生硬的commit id。里程碑的命令是git tag
,可以创建、删除和查看里程碑。
在Git中还有一个git stash
命令,用于保存当前的工作进度,相比之下,git stash
可以在任何操作之后使用该命令,当需要处理其他操作的时候再接着上次的进度继续操作。里程碑所关联的commit是具有一定意义的,通常是在完成某个feature或者bug修复的时候使用,这也是称之为里程碑的原因。
以下创建里程碑的命令实例仍然基于上一篇文章使用的仓库进行。在正式开始之前,在user1和user2下的project目录执行
git pull
。
git tag
创建里程碑的常用命令如下:
//创建轻量级里程碑
git tag <tagname> [<commit>]
//创建带说明的里程碑
git tag -m <message> <tagname> [<commit>]
//创建带GnuPG签名的里程碑
git tag -s -m <message> <tagname> [<commit>]
轻量级里程碑
所谓轻量级里程碑是指在创建里程碑的时候无须输入描述信息。
在user2目录创建一个轻量级里程碑执行如下命令:
//创建一个空提交
git commit --allow-empty -m "blank commit."
//创建轻量级里程碑
git tag mytag
//查看创建的里程碑
git tag -l my*(也可以直接使用命令 git tag显示所有创建的里程碑)
在创建轻量级里程碑的时候会在版本库的.git/refs/tags
目录下创建一个新文件mytag
,该文件就是刚才创建的轻量级里程碑mytag
。该里程碑的信息可以使用如下命令查看:
//查看.git/refs/tags下的文件
ls -l
//查看这个文件引用的内容
cat .git/refs/tags/mytag
得到的结果是:
mytag
abb48d53ec4096f82e1014aa72e015610fa21415
可以发现是一个SHA-1的哈希值,那么该哈希值也就是该里程碑对应的什么类型的对象呢?使用如下命令:
//加上-t可以显示引用对应的类型
git cat-file -t mytag
输出结果为:
commit
发现刚才创建的mytag里程碑是一个commit,再继续查看该commit的内容。使用如下命令:
//加上-p参数表示查看的是内容
git cat-file -p mytag
tree 026ce5f7b29dcd6e8760c1fd72fbd85536c98a49
parent 1e5c65640c4154866c87f20aa4fb78756cc49833
author rhwayfun [email protected] 1461832874 +0800
committer rhwayfun [email protected] 1461832874 +0800
blank commit.
显示了该commit对应的tree对象是026ce5f,父提交是1e5c656,还有提交的作者信息。
然而,在使用git tag
创建里程碑的时候不推荐创建轻量级里程碑,因为轻量级里程碑无法知道是谁创建的里程碑(上面显示了提交的作者信息,那是我配置过的原因),何时创建的。还有一点就是在使用git describe
命令(该命令可以显示生成的版本描述字符串)无法得到里程碑生成的字符串,而在创建里程碑时添加描述字符串的好处是对版本的开发流程一目了然,commit是无法做到这一点的。
带说明的里程碑
为了克服轻量级里程碑的缺点,在项目开发中使用较多的还是带说明的里程碑和带GnuPG签名的里程碑。带说明的里程碑就是在创建里程碑的时候提供一个关于该里程碑的说明。具体演示如下:
//创建一个空白提交
git commit --allow-empty -m "blank commit for annotated tag test."
//创建带说明的里程碑
git tag -m "My first annotated tag." mytag2
//查看创建的里程碑,-n<num>表示输出<num>行带说明的里程碑,是指将里程碑的说明在<num>行之内的显示出来,并不是指输出的里程碑本身的行数
git tag -l -n1
//显示里程碑的描述字符串
git describe
执行git tag -l -n1
后输出的结果如下:
mytag blank commit.
mytag2 My first annotated tag.
这样就成功创建一个名为mytag2
的带说明的里程碑。要注意的是带说明的里程碑除了在创建里程碑的时候附带了说明字符串,而且mytag2不是一个commit了,而是一个tag对象。使用如下命令可以看到:
//查看mytag2的类型
cat .git/refs/tags/mytag2
//查看mytag2的内容
git cat-file -p mytag2
依次输出:
tag
表明mytag2是一个tag类型的对象
object 6d095e6112f25444729ee8cddf8912c626dceca1
type commit
tag mytag2
tagger rhwayfun [email protected] 1461833122 +0800
My first annotated tag.
由此可见,在创建带说明的里程碑的时候会在版本库中生成一个tag对象,这个对象记录了创建该里程碑的相关信息,这些信息包括创建的时间、作者和描述字符串。
带GnuPG签名的里程碑
该里程碑与带说明的里程碑本质是一样的,只不过在创建里程碑的时候还额外添加了GnuPG签名。具体创建带GnuPG签名的里程碑操作如下:
//推送最新修改到远程仓库,该操作并不会将创建的里程碑也推送到远程仓库。这样设计是为了尽可能保持各自本地仓库的独立性,而需要也把里程碑推送到远程仓库则需要手动添加
git push
//手动添加创建的里程碑到远程仓库
git push origin refs/tags/*
//如果只是添加某一个里程碑则使用如下命令
git push origin mytag
执行以上的操作后就把本地所创建的里程碑推送到了远程仓库,而如果需要查看远端仓库的里程碑,可以执行如下命令:
//显示远端仓库以my开头的里程碑
git ls-remote origin my*
显示结果如下:
abb48d53ec4096f82e1014aa72e015610fa21415 refs/tags/mytag
ec3edf736cd6e37431546b36d2623659995e005e refs/tags/mytag2
6d095e6112f25444729ee8cddf8912c626dceca1 refs/tags/mytag2^{}
可以看到,之前创建的两个里程碑mytag
和mytag2
都推送到了远端仓库。
下面开始创建带GnuPG签名的里程碑:
//仍然创建一个空提交
git commit --allow-empty -m "blank commit for GnuPG-signed tag test."
//开始创建里程碑
git tag -s -m "My first GPG-signed tag." mytag3
执行失败,信息如下:
gpg: directory
/home/rhwayfun/.gnupg' created
/home/rhwayfun/.gnupg/gpg.conf’ created
gpg: new configuration file
gpg: WARNING: options in/home/rhwayfun/.gnupg/gpg.conf' are not yet active during this run
/home/rhwayfun/.gnupg/secring.gpg’ created
gpg: keyring
gpg: keyring `/home/rhwayfun/.gnupg/pubring.gpg’ created
gpg: skipped “rhwayfun [email protected]”: secret key not available
gpg: signing failed: secret key not available
error: gpg failed to sign the data
error: unable to sign the tag
以上信息告诉我们找不到rhwayfun [email protected]签名可用的公钥和私钥对,所以需要首先创建公钥。
//查看可用的公钥,需要首先安装gnupg
gpg --list-keys
//生成key
gpg --gen-key
生成key之后再次执行gpg --list-keys
就可以看到如下的输出:
/home/rhwayfun/.gnupg/pubring.gpg
———————————
pub 2048R/24694C17 2016-04-28 [expires: 2016-04-29]
uid user2 [email protected]
sub 2048R/235AE980 2016-04-28 [expires: 2016-04-29]
这样就说明成功创建了公钥/私钥对。重新创建里程碑:
git tag -s -m "My first GPG-signed tag." mytag3
//查看创建的里程碑
git tag -l my* -n1
输出如下:
mytag blank commit.
mytag2 user2 update this annotated tag.
mytag3 My first GPG-signed tag.
这样就成功创建了带签名的里程碑。
删除本地仓库的里程碑非常之简单,只需要执行如下命令就可以删除。
git tag -d mytag2
如果发现删除错误,可以使用如下命令进行补救:
//ec3edf7该tag指向的commit
git tag mytag2 ec3edf7
而如果需要删除远端仓库的里程碑则需要使用如下的命令:
git push <remote_url> :<tagname>
比如在user1工作区下,需要删除远端的mytag2里程碑,那么只需要执行如下命令:
git push origin :mytag2
这样,就完成了远端里程碑的删除。
通过实际操作的方式演示了如何创建三种不同类型的里程碑,以及如何删除本地以及远程仓库的里程碑。不过虽然可以对里程碑进行删除更新等操作,但是里程碑不要应该随意修改,因为里程碑从概念上讲是对历史提交的一个标记,是具有一定意义的。而且,随意修改里程碑可能会造成里程碑管理的混乱。在实际的项目开发中只能允许部分人员具有推送远程里程碑的权限。