2012/08/14

说说git中tag的实现

今天下午跟Yi姐聊到git中tag的实现,整理成文如下。

tag的实现
在git中,常见的tag有两种,一种是轻量级的tag,一种是带说明的tag。

要创建一个新的tag是一个很简单的操作,创建轻量级tag只需git tag [tagname] 创建带说明的tag只需git tag [tagname] -m [message]。看似两者差别很小,但背后,在git内部,二者的实现却差别很大。

首先通过创建两个tag来开始今天的讨论:
gewang@LM-SHC-00355679@17:01:40:~/test/TryOurInternalGithub
=> git log --oneline -1 # 查看当前所在commit
ab2d6ee 2nd commit
gewang@LM-SHC-00355679@16:34:14:~/test/TryOurInternalGithub
=> find .git/objects/  
.git/objects/
.git/objects//info
.git/objects//info/packs
.git/objects//pack
.git/objects//pack/pack-37b8c10ba135fb7e9166a0ea483ab9597b15d039.idx
.git/objects//pack/pack-37b8c10ba135fb7e9166a0ea483ab9597b15d039.pack
gewang@LM-SHC-00355679@16:34:17:~/test/TryOurInternalGithub
=> git tag tag1 # 创建轻量级的tag1
gewang@LM-SHC-00355679@16:34:23:~/test/TryOurInternalGithub
=> find .git/objects/ # 没有新的object生成
.git/objects/
.git/objects//info
.git/objects//info/packs
.git/objects//pack
.git/objects//pack/pack-37b8c10ba135fb7e9166a0ea483ab9597b15d039.idx
.git/objects//pack/pack-37b8c10ba135fb7e9166a0ea483ab9597b15d039.pack
gewang@LM-SHC-00355679@16:34:26:~/test/TryOurInternalGithub
=> git tag tag2 -m "tag2" # 创建一个带说明的提交
gewang@LM-SHC-00355679@16:34:42:~/test/TryOurInternalGithub
=> find .git/objects/ # 生成了一个新的object 84458d
.git/objects/
.git/objects//84
.git/objects//84/458d8d078307134b8f6712d84c07f438ba6657
.git/objects//info
.git/objects//info/packs
.git/objects//pack
.git/objects//pack/pack-37b8c10ba135fb7e9166a0ea483ab9597b15d039.idx
.git/objects//pack/pack-37b8c10ba135fb7e9166a0ea483ab9597b15d039.pack

可以看到,二者深层次的差别在于是否会创建一个object来记录这个tag。到git repo中的tag命名空间查看:
gewang@LM-SHC-00355679@16:59:23:~/test/TryOurInternalGithub
=> ls .git/refs/tags/
tag1 tag2
接着查看两者的内容:
gewang@LM-SHC-00355679@17:00:34:~/test/TryOurInternalGithub
=> cat .git/refs/tags/tag1
ab2d6eea3cde6b1fac1784a6afb19a552a78689e
gewang@LM-SHC-00355679@17:01:14:~/test/TryOurInternalGithub
=> cat .git/refs/tags/tag2
84458d8d078307134b8f6712d84c07f438ba6657

可以看到轻量级的tag1的内容直接指向了当前的commit id。而带提交的tag2则指向了那个新生成的object。让我们来看看这个新object的真面目。
gewang@LM-SHC-00355679@17:04:17:~/test/TryOurInternalGithub
=> git cat-file -t 84458d8d078307134b8f6712d84c07f438ba6657
tag
gewang@LM-SHC-00355679@17:04:22:~/test/TryOurInternalGithub
=> git cat-file -p 84458d8d078307134b8f6712d84c07f438ba6657
object ab2d6eea3cde6b1fac1784a6afb19a552a78689e
type commit
tag tag2
tagger Michael Wang  Tue Aug 14 16:34:42 2012 +0800

tag2
可以看到新生成的object的类型是tag(这是git四个基本object类型中的一种,其他三种是commit,tree,blob)。可以看到这个object记录的5条信息,它指向了一个id为ab2d6eea3的对象,这个对象的类型为commit,这个tag的名字叫tag2,打tag的username是Michael,当天给的message是“tag2”。

至此,基本可以看出git中tag的实现方式,轻量级的tag直接通过一个tag文件指向一个commit id。带说明的tag会生成一个特定的tag对象,记录一系列和tag相关的信息。

那么实际操作中我们该使用哪种tag呢?一般情况下推荐使用带提交的tag,因为它除了基本的信息外,还记录了谁在什么时间创建这个tag,以及一些说明,这对以后查阅历史提供了更详尽的信息。

与server端交互
现在尝试将新创建的tag推送到server:
gewang@LM-SHC-00355679@17:04:25:~/test/TryOurInternalGithub
=> git push
Everything up-to-date
git提示我们所有的refs都已经up-to-date,表示git默认不会推送本地tag到server端,必须明确指定要推送的tag:
gewang@LM-SHC-00355679@17:16:24:~/test/TryOurInternalGithub
=> git push origin tag1
Total 0 (delta 0), reused 0 (delta 0)
To git@github.scm.corp.ebay.com:gewang/TryOurInternalGithub.git
 * [new tag]         tag1 -> tag1
gewang@LM-SHC-00355679@17:17:38:~/test/TryOurInternalGithub
=> git push origin tag2
Counting objects: 1, done.
Writing objects: 100% (1/1), 158 bytes, done.
Total 1 (delta 0), reused 0 (delta 0)
To git@github.scm.corp.ebay.com:gewang/TryOurInternalGithub.git
 * [new tag]         tag2 -> tag2
在上边的例子中可以看到推送tag2时,向server端写了1个object,而tag1则没有,这也印证了2者的区别。

那么要如何才能从server获得tag呢?我们删除本地tag,执行git pull看看:
gewang@LM-SHC-00355679@17:20:45:~/test/TryOurInternalGithub
=> git tag -d tag1
Deleted tag 'tag1' (was ab2d6ee)
gewang@LM-SHC-00355679@17:20:54:~/test/TryOurInternalGithub
=> git tag -d tag2
Deleted tag 'tag2' (was 84458d8)
gewang@LM-SHC-00355679@17:20:54:~/test/TryOurInternalGithub
=> git pull
remote: Counting objects: 1, done.
remote: Total 1 (delta 0), reused 1 (delta 0)
Unpacking objects: 100% (1/1), done.
From github.scm.corp.ebay.com:gewang/TryOurInternalGithub
 * [new tag]         tag1       -> tag1
 * [new tag]         tag2       -> tag2
可以看到默认清空下,git pull会将server端的tag获取到本地。

以上就是关于git中tag的简要分析,有问题的朋友可以留言说明,我会尽快update。

没有评论:

发表评论