17.5 共享里程碑

    现在看看用户user1的工作区状态。可以看出现在的工作区相比上游有三个新的提交。


    $git status #On branch master #Your branch is ahead of 'origin/master' by 3 commits. # nothing to commit(working directory clean)

    那么如果执行git push命令向上游推送,会将本地创建的三个里程碑推送到上游吗?通过下面的操作来试一试。

    向上游推送。


    $git push Counting objects:3,done. Delta compression using up to 2 threads. Compressing objects:100%(3/3),done. Writing objects:100%(3/3),512 bytes,done. Total 3(delta 0),reused 0(delta 0) Unpacking objects:100%(3/3),done. To file:///path/to/repos/hello-world.git 3e6070e..ebcf6d6 master->master

    通过执行git ls-remote可以查看上游版本库的引用,会发现本地建立的三个里程碑,并没有推送到上游。


    $git ls-remote origin my*

    创建的里程碑,默认只在本地版本库中可见,不会因为对分支执行推送而将里程碑也推送到远程版本库。这样的设计显然更为合理,否则的话,每个用户本地创建的里程碑都自动向上游推送,那么上游的里程碑将有多么杂乱,而且不同用户创建的相同名称的里程碑会互相覆盖。

    1.显式推送以共享里程碑

    如果用户确实需要将某些本地建立的里程碑推送到远程版本库,需要在git push命令中明确地表示出来。下面在用户user1的工作区执行命令,将mytag里程碑共享到上游版本库。


    $git push origin mytag Total 0(delta 0),reused 0(delta 0) To file:///path/to/repos/hello-world.git *[new tag]mytag->mytag

    如果需要将本地建立的所有里程碑全部推送到远程版本库,可以使用通配符。


    $git push origin refs/tags/* Counting objects:2,done. Delta compression using up to 2 threads. Compressing objects:100%(2/2),done. Writing objects:100%(2/2),687 bytes,done. Total 2(delta 0),reused 0(delta 0) Unpacking objects:100%(2/2),done. To file:///path/to/repos/hello-world.git *[new tag]mytag2->mytag2 *[new tag]mytag3->mytag3

    再用命令git ls-remote查看上游版本库的引用,会发现本地建立的三个里程碑,已经能够在上游中看到了。


    $git ls-remote origin my* 60a2f4f31e5dddd777c6ad37388fe6e5520734cb refs/tags/mytag 149b6344e80fc190bda5621cd71df391d3dd465e refs/tags/mytag2 8a9f3d16ce2b4d39b5d694de10311207f289153f refs/tags/mytag2^{} 5dc2fc52f2dcb84987f511481cc6b71ec1b381f7 refs/tags/mytag3 ebcf6d6b06545331df156687ca2940800a3c599d refs/tags/mytag3^{}

    2.用户从版本库执行拉回操作,会自动获取里程碑么?

    用户user2的工作区中如果执行git fetch或git pull操作,能自动将用户user1推送到共享版本库中的里程碑获取到本地版本库么?下面实践一下。

    (1)进入user2的工作区。


    $cd/path/to/user2/workspace/hello-world/

    (2)执行git pull命令,从上游版本库获取提交。


    $git pull remote:Counting objects:5,done. remote:Compressing objects:100%(5/5),done. remote:Total 5(delta 0),reused 0(delta 0) Unpacking objects:100%(5/5),done. From file:///path/to/repos/hello-world 3e6070e..ebcf6d6 master->origin/master *[new tag]mytag3->mytag3 From file:///path/to/repos/hello-world *[new tag]mytag->mytag *[new tag]mytag2->mytag2 Updating 3e6070e..ebcf6d6 Fast-forward

    (3)可见执行git pull操作,能够在获取远程共享版本库的提交的同时,获取新的里程碑。下面的命令可以看到本地版本库中的里程碑。


    $git tag-n1-l my* mytag blank commit. mytag2 My first annotated tag. mytag3 My first GPG-signed tag.

    3.里程碑变更能够自动同步吗?

    里程碑可以被强制更新。当里程碑被改变后,已经获取到里程碑的版本库再次使用获取或拉回操作,能够自动更新里程碑吗?答案是不能。可以看看下面的操作。

    (1)用户user2强制更新里程碑mytag2。


    $git tag-f-m "user2 update this annotated tag." mytag2 HEAD^ Updated tag 'mytag2'(was 149b634)

    (2)里程碑mytag2已经是不同的对象了。


    $git rev-parse mytag2 0e6c780ff0fe06635394db9dac6fb494833df8df $git cat-file-p mytag2 object 8a9f3d16ce2b4d39b5d694de10311207f289153f type commit tag mytag2 tagger user2<user2@moon.ossxp.com>Mon Jan 3 01:14:18 2011+0800 user2 update this annotated tag.

    (3)为了更改远程共享服务器中的里程碑,同样需要显式推送。即在推送时写上要推送的里程碑名称。


    $git push origin mytag2 Counting objects:1,done. Writing objects:100%(1/1),171 bytes,done. Total 1(delta 0),reused 0(delta 0) Unpacking objects:100%(1/1),done. To file:///path/to/repos/hello-world.git 149b634..0e6c780 mytag2->mytag2

    (4)切换到另外一个用户user1的工作区。


    $cd/path/to/user1/workspace/hello-world/

    (5)用户user1执行拉回操作,没有获取到新的里程碑。


    $git pull Already up-to-date.

    (6)用户user1必须显式地执行拉回操作。即要在git pull的参数中使用引用表达式。

    所谓引用表达式就是用冒号分隔的引用名称或通配符。用在这里代表用远程共享版本库的引用refs/tag/mytag2覆盖本地版本库的同名引用。


    $git pull origin refs/tags/mytag2:refs/tags/mytag2 remote:Counting objects:1,done. remote:Total 1(delta 0),reused 0(delta 0) Unpacking objects:100%(1/1),done. From file:///path/to/repos/hello-world -[tag update]mytag2->mytag2 Already up-to-date.

    关于里程碑的共享和同步操作,看似很烦琐,但用心体会就会感觉到Git关于里程碑共享的设计是非常合理和人性化的:

    里程碑共享,必须显式的推送。即在推送命令的参数中,标明要推送哪个里程碑。

    显式推送是防止用户随意推送里程碑导致共享版本库中里程碑泛滥的方法。当然还可以参考第5篇“第30章Gitolite服务架设”的相关章节为共享版本库添加授权,只允许部分用户向服务器推送里程碑。

    执行获取或拉回操作,自动从远程版本库获取新里程碑,并在本地版本库中创建。

    获取或拉回操作,只会将获取的远程分支所包含的新里程碑同步到本地,而不会将远程版本库的其他分支中的里程碑获取到本地。这既方便了里程碑的取得,又防止本地里程碑因同步远程版本库而泛滥。

    如果本地已有同名的里程碑,默认不会从上游同步里程碑,即使两者里程碑的指向是不同的。理解这一点非常重要。这也就要求里程碑一旦共享,就不要再修改。