第 2 章 Mercurial 教程: 基础知识

目录

2.1. 安装 Mercurial
2.1.1. Windows
2.1.2. Mac OS X
2.1.3. Linux
2.1.4. Solaris
2.2. 开始
2.2.1. 内置帮助
2.3. 使用版本库
2.3.1. 创建版本库的工作副本
2.3.2. 什么是版本库?
2.4. 回溯历史
2.4.1. 变更集,版本,与其它用户交互
2.4.2. 查看指定版本
2.4.3. 更详细的信息
2.5. 命令选项
2.6. 创建和复审变更
2.7. 在新修改集中记录修改
2.7.1. 配置用户名称
2.7.2. 写提交日志
2.7.3. 写高质量的提交日志
2.7.4. 终止提交
2.7.5. 欣赏我们的成果
2.8. 分享修改
2.8.1. 从其它版本库取得变更
2.8.2. 更新工作目录
2.8.3. 发布修改到其它版本库
2.8.4. 默认位置
2.8.5. 通过网络共享修改
2.9. 开始新项目

2.1. 安装 Mercurial

对于每种流行的操作系统,都有已经构建的二进制软件包。这让在你的计算机上开始使用 Mercurial 变得很容易。

2.1.1. Windows

Windows 中最好的 Mercurial 版本是TortoiseHg,它的主页地址是 http://bitbucket.org/tortoisehg/stable/wiki/Home。这个软件没有外部依赖,它可以独立工作,同时提供了命令行和图形用户界面。

2.1.2. Mac OS X

Lee Cantey 为 Mac OS X 在 http://mercurial.berkwood.com 发布了 Mercurial 安装程序。

2.1.3. Linux

由于每种 Linux 发行版都有自己的包管理工具,开发策略和进度,从而很难给出安装 Mercurial 二进制包的全面说明。你安装的 Mercurial 版本,在很大程度上依赖于你所使用的发行版的 Mercurial 维护者的活跃程度。

为了让事情简单,我会致力于说明在最流行的 Linux 发行版中,从命令行安装 Mercurial 的方法。这些发行版都提供了图形界面的包管理器,让你通过点击鼠标安装 Mercurial;寻找的包名称是 mercurial

  • Ubuntu 与 Debian:

    apt-get install mercurial
  • Fedora:

    yum install mercurial
  • OpenSUSE:

    zypper install mercurial
  • Gentoo:

    emerge mercurial

2.1.4. Solaris

位于 http://www.sunfreeware.com 的 SunFreeWare 提供了 Mercurial 的二进制安装包。

2.2. 开始

首先,我们使用 hg version 命令检查 Mercurial 是否已经正确安装。它打印出来的实际版本信息并不重要;我们只关心它是否能够运行,打印出信息。

$ hg version
Mercurial Distributed SCM (version 1.3.1)

Copyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

2.2.1. 内置帮助

Mercurial 内置了帮助系统。当你不记得如何执行一个命令时,它会给你重要的帮助。如果你完全没有头绪,那就直接运行 hg help;它会给出命令的简短列表,还描述了每个命令的作用。如果你需要具体命令的帮助(下述),它会给出更详细的信息。

$ hg help init
hg init [-e CMD] [--remotecmd CMD] [DEST]

create a new repository in the given directory

    Initialize a new repository in the given directory. If the given
    directory does not exist, it will be created.

    If no directory is given, the current directory is used.

    It is possible to specify an ssh:// URL as the destination.
    See 'hg help urls' for more information.

options:

 -e --ssh        specify ssh command to use
    --remotecmd  specify hg command to run on the remote side

use "hg -v help init" to show global options

要获得更多的详细信息(通常不需要),可以执行 hg help -v。选项 -v--verbose 的短格式,告诉 Mercurial 要打印通常不需要的更多信息。

2.3. 使用版本库

在Mercurial中,所有的操作都在版本库中进行。项目的版本库包括了属于该项目的所有文件和这些文件的历史记录。

版本库没有什么神秘的地方;仅仅是你系统中的一个目录树,Mercurial会将它们特殊处理。你可以在任何喜欢的时候使用命令行或者文件浏览器删除版本库或者给它改名。

2.3.1. 创建版本库的工作副本

拷贝版本库有点特殊。虽然你可以使用文件拷贝命令来复制一般版本库,最好还是用Mercurial内置的命令。这个命令叫做 hg clone,因为它创建了一个原来版本库的拷贝。

$ hg clone http://hg.serpentine.com/tutorial/hello
destination directory: hello
requesting all changes
adding changesets
adding manifests
adding file changes
added 5 changesets with 5 changes to 2 files
updating working directory
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

如上所示,使用hg clone的好处在于它能够让你通过网络克隆版本库。另外一个好处你它会记得这个版本库是从哪里克隆的,稍后会看到,当我们想从其他的版本库获取新的变更的时候这点这会非常有用。

如果我们克隆成功,我们会得到一个本地目录,叫做 hello。这个目录会包括一些文件。

$ ls -l
total 0
drwxr-xr-x 3 dongsheng g11n 45 Oct 23 01:38 hello
$ ls hello
Makefile  hello.c

这个版本库中的文件和我们刚才克隆的版本库中的文件相同的内容和版本历史

每个Mercurial版本库都是完整的,自包含的,独立的。它包含了项目文件的一份私有拷贝和全部历史。我们刚才已经提到,克隆的版本库会记住它克隆的那个版本库的地址,但是Mercurial不会和那个或者其他任何一个版本库通信,除非你给它命令。

这意味着,我们可以随意的在我们的版本库中做实验,非常安全,因为它是一个私有的沙盒,不会影响任何人。

2.3.2. 什么是版本库?

当我们仔细观察版本库内部时,我们会发现它有一个叫.hg的目录。这就是Mercurial为版本库保存所有元数据的地方。

$ cd hello
$ ls -a
.  ..  .hg  Makefile  hello.c

目录.hg中的内容和其子目录是Mercurial私有的。版本库中的其他任何文件和目录你都可以随意操作。

介绍一点术语, .hg目录是真正的版本库,所有其他的文件和目录称为工作目录一个简单的区分的方法就是版本库中包含了项目的历史,而 工作目录则是项目组一个特定历史点上的快照

2.4. 回溯历史

对于一个新的,我们不熟悉的版本库,我们想做的第一件事就是了解它的历史。命令hg log可以让我们浏览版本库中的历史变更。

$ hg log
changeset:   4:2278160e78d4
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:16:53 2008 +0200
summary:     Trim comments.

changeset:   3:0272e0d5a517
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:08:02 2008 +0200
summary:     Get make to generate the final binary from a .o file.

changeset:   2:fef857204a0c
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:05:04 2008 +0200
summary:     Introduce a typo into hello.c.

changeset:   1:82e55d328c8c
user:        mpm@selenic.com
date:        Fri Aug 26 01:21:28 2005 -0700
summary:     Create a makefile

changeset:   0:0a04b987be5a
user:        mpm@selenic.com
date:        Fri Aug 26 01:20:50 2005 -0700
summary:     Create a standard "hello, world" program

缺省情况下,这个命令对项目中记录的每个变更都输出一段简介,在Mercurial的术语中,我们将这些记录的事件成为变更集,因为每个记录都可能包括几个文件的变更。

hg log输出记录的各个字段的意义如下。

  • changeset:这个字段包括一个数字,接着一个冒号,然后是是一个十六进制字符串。这是这个变更的标识符。十六进制字符串是唯一标识符:在这个版本库的任何一个拷贝中,同一个字符串总是对应同一个变更集。数字更短并且比十六进制字符串更容易书写,但它不是唯一的:一个版本库的两个不同的的克隆中的同一个数字可能对应不同的变更集。

  • user:这个字段标识是谁创建了这个变更集。这个字段格式可以自由定义,通常是一个人的姓名加上电子邮件地址。

  • date:这是变更集创建的日期和时间,还有时区。(日期和时间是相对于时区的;他们显示了创建变更的人的日期和时间。)

  • summary:创建者对该变更集的描述的第一行文本信息。

  • 有些变更集,像上面的第一个,有一个标签字段。标签是区分变更集的另外一种方法,给它一个容易记得名字。(叫做tip的标签有特殊意义:总是指向版本库中的最新的变更。)

缺省情况下,hg log的输出仅仅是个摘要,没有更详细的信息。

图 2.1 “版本库 hello 的历史图”以图形化方式显示了版本库hello的历史,这样很容易看出历史的流向。在本章和下面的章节中,我们会多次使用这个图。

图 2.1. 版本库 hello 的历史图

XXX add text

2.4.1. 变更集,版本,与其它用户交互

英语是一种非常随便的语言,计算机史上也向来以混乱的术语为荣(如能用四个词为什么要用一个呢?),就版本控制而言,有很多词和短语有相同的意思。如果你和别人讨论Mercurial版本库的历史,你会发现变更集这个词常常被简化成变更或者(写的时候)cset,有时候变更集也指一个版本或者rev

实际上,用哪个来描述变更集的概念并不重要,重要的是如何用标识符来标识一个特定的变更集。回忆一下hg log命令的输出,changeset字段里用一个数字和一个十六进制字符串来标识一个变更集。

  • 版本号是一个方便的标识,它仅在当前版本库中有效

  • 十六进制字符串是永久的,不变的标识,它在该版本库的所有拷贝中都指向同一个变更集。

这个区别很重要。如果你通过邮件和别人讨论版本33,很有可能他们的版本33和你的不一样。原因在于版本号依赖于相应变更进入版本库的顺序。,不能保证同一个变更在不同的版本库中会有相同的次序。有可能三个变更a,b,c在一个版本库中的次序是0,1,2,而在另外一个版本库中则变成0,2,1

Mercurial使用版本号纯粹是为了有些命令的方便,如果你要和别人讨论变更集,或者由于某些原因为一个变更集做记录(如在bug报告中),请使用十六进制标识符。

2.4.2. 查看指定版本

如果只想用hg log查看一个版本的日志使用-r(或者--rev)选项。版本号和十六进制标识符都可以来指定版本,可以一次指定任意多个版本。

$ hg log -r 3
changeset:   3:0272e0d5a517
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:08:02 2008 +0200
summary:     Get make to generate the final binary from a .o file.

$ hg log -r 0272e0d5a517
changeset:   3:0272e0d5a517
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:08:02 2008 +0200
summary:     Get make to generate the final binary from a .o file.

$ hg log -r 1 -r 4
changeset:   1:82e55d328c8c
user:        mpm@selenic.com
date:        Fri Aug 26 01:21:28 2005 -0700
summary:     Create a makefile

changeset:   4:2278160e78d4
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:16:53 2008 +0200
summary:     Trim comments.

如果你想显示几个版本历史,但是不想一个一个的列出来,可以使用 范围标记;它会显示包括abcdef,以及它们之间的所有版本的版本历史。

$ hg log -r 2:4
changeset:   2:fef857204a0c
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:05:04 2008 +0200
summary:     Introduce a typo into hello.c.

changeset:   3:0272e0d5a517
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:08:02 2008 +0200
summary:     Get make to generate the final binary from a .o file.

changeset:   4:2278160e78d4
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:16:53 2008 +0200
summary:     Trim comments.

Mercurial还可以指定版本的输出顺序,如hg log -r 2:4输出2,3,4。而hg log -r 4:2则输出4,3,2。

2.4.3. 更详细的信息

当你知道你在找那个版本的时候,hg log输出的摘要是非常有用的,但有时候你不知道要找哪个版本,你想看到变更的完整描述,或者修改过的文件的列表,hg log命令的-v--verbose)选项会给出更详细的信息。

$ hg log -v -r 3
changeset:   3:0272e0d5a517
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:08:02 2008 +0200
files:       Makefile
description:
Get make to generate the final binary from a .o file.


如果你想同时看到变更的描述和内容,增加-p--patch)选项。这会将变更的内容以unified diff的格式显示(如果你不知道unified diff,请参考第 12.4 节 “理解补丁”

$ hg log -v -p -r 2
changeset:   2:fef857204a0c
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:05:04 2008 +0200
files:       hello.c
description:
Introduce a typo into hello.c.


diff -r 82e55d328c8c -r fef857204a0c hello.c
--- a/hello.c	Fri Aug 26 01:21:28 2005 -0700
+++ b/hello.c	Sat Aug 16 22:05:04 2008 +0200
@@ -11,6 +11,6 @@
 
 int main(int argc, char **argv)
 {
-	printf("hello, world!\n");
+	printf("hello, world!\");
 	return 0;
 }

-p选项非常有用,所以一定要记住。

2.5. 命令选项

我们休息一下,先不讨论Mercurial命令了,而是来看看它们工作的模式;这对以后的学习会非常有帮助。

Mercurial处理传递给它的命令选项的方法简单一致。它遵从现代Linux和Unix对选项的处理习惯。

  • 每个选项有一个长名。例如,我们已经看到了,hg log命令接受--rev选项。

  • 大多数选项还有短名,除了使用--rev,我们还可以用-r。(有些选项没有短名的原因是他们很少用到。)

  • Long options start with two dashes (e.g. --rev), while short options start with one (e.g. -r).

  • Option naming and usage is consistent across commands. For example, every command that lets you specify a changeset ID or revision number accepts both -r and --rev arguments.

  • 如果使用短选项,你可以把它们放在一起以减少输入。例如,命令hg log -v -p -r 2 可以写成 hg log -vpr2

在本书的例子中,我通常使用短选项,很少用长选项。这仅仅是我个人的习惯,你不一定要这样。

Most commands that print output of some kind will print more output when passed a -v (or --verbose) option, and less when passed -q (or --quiet).

[注意] 选项命名的一致性

Almost always, Mercurial commands use consistent option names to refer to the same concepts. For instance, if a command deals with changesets, you'll always identify them with --rev or -r. This consistent use of option names makes it easier to remember what options a particular command takes.

2.6. 创建和复审变更

现在我们已经对查看Mercurial的版本历史有了一些了解,现在我们开始做些修改并且检查这些修改。

The first thing we'll do is isolate our experiment in a repository of its own. We use the hg clone command, but we don't need to clone a copy of the remote repository. Since we already have a copy of it locally, we can just clone that instead. This is much faster than cloning over the network, and cloning a local repository uses less disk space in most cases, too[1].

$ cd ..
$ hg clone hello my-hello
updating working directory
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd my-hello

说句题外话,保留远程版本库的一份原始拷贝是一个很好的习惯,这样你就可以为每个任务都创建临时的克隆作为沙盒。直到认务完成并且你准备好提交到版本库, 每个任务都和其他的独立,这样你可以并行工作。因为本地的克隆很方便,在任何时候克隆和销毁一个版本库都只有很小的开销。

在我们的my-hello版本库中,有一个叫hello.c的文件,它包含了经典的hello, world程序。

$ cat hello.c
/*
 * Placed in the public domain by Bryan O'Sullivan.  This program is
 * not covered by patents in the United States or other countries.
 */

#include <stdio.h>

int main(int argc, char **argv)
{
	printf("hello, world!\");
	return 0;
}

我们编辑这个文件,让它打印第二行输出。

# ... edit edit edit ...
$ cat hello.c
/*
 * Placed in the public domain by Bryan O'Sullivan.  This program is
 * not covered by patents in the United States or other countries.
 */

#include <stdio.h>

int main(int argc, char **argv)
{
	printf("hello, world!\");
	printf("hello again!\n");
	return 0;
}

Mercurial的hg status命令能告诉我们它对版本库中的文件有多少了解。

$ ls
Makefile  hello.c
$ hg status
M hello.c

hg status命令对有些文件没有输出信息,但是对文件hello.c,有一行以M为开头的输出。除非你明确告诉它,命令hg status不会输出那些没有修改的文件的信息。

M表明Mercurial已经发现我们修改了hello.c。我们不需要在改文件之前,或者在修改完之后通知Mercurial;它自己能处理。

知道文件hello.c被修改了很有用,但有时候我们想知道做了什么样的修改。这时,我们应该使用 hg diff命令。

$ hg diff
diff -r 2278160e78d4 hello.c
--- a/hello.c	Sat Aug 16 22:16:53 2008 +0200
+++ b/hello.c	Fri Oct 23 01:38:05 2009 +0000
@@ -8,5 +8,6 @@
 int main(int argc, char **argv)
 {
 	printf("hello, world!\");
+	printf("hello again!\n");
 	return 0;
 }
[提示] 理解补丁

如果你不知道如何理解以上信息,请参考第 12.4 节 “理解补丁”

2.7. 在新修改集中记录修改

我们可以修改文件,创建并测试我们的修改,使用命令hg statushg diff复审修改,直到我们对修改满意,同时也达到了一个自然的停止点,然后用一个新的变更集记录我们的工作。

我们用命令hg commit创建新的变更集;我们通常把这个称为做一次提交或者提交

2.7.1. 配置用户名称

当你准备第一次运行hg commit命令时,不一定会成功。对于你提交的每个变更,Mercurial都会记录你的名字和邮件地址,这样你和其他人以后就能分开是谁做的哪个变更。Mercurial会自动尝试找出一个有意义的用户名来提交。它会依次尝试以下方法:

  1. 如果你给hg commit命令加上了-u选项,接着是一个用户名,Mercurial会使用这个用户名,这是最高的优先级。

  2. 接下来测试你是否设定了HGUSER环境变量。

  3. 如果你在主目录创建了名字为.hgrc的文件,其中包括username条目,那就用它。如果想知道这个文件的格式,请参考下面的第 2.7.1.1 节 “创建 Mercurial 的配置文件”

  4. 如果你设置了EMAIL环境变量,那就用它。

  5. Mercurial会查询你的系统,找出主机名和你的用户名,然后用他们创建一个用户名。这样的用户名不怎么有用,所以在只能这样生成用户名的时候,它会打印出一条告警信息。

如果所有的这些机制都失败了,Mercurial会执行失败退出,打印出一条错误信息这种情况下,只有你设定了用户名之后才能提交。

当你需要覆盖Mercurial缺省的用户时,可以考虑HGUSER环境变量和hg commit命令的-u选项。正常使用的情况下,最简单实用的方法就是创建.hgrc文件来设定用户名;步骤如下。

2.7.1.1. 创建 Mercurial 的配置文件

设定用户名的时候,使用你最喜欢的编辑器在你的主目录创建一个名为.hgrc的文件。Mercurial将会从这个文件中查找你的个人配置信息。你的.hgrc开始的时候应该是这样子。

[提示] Windows上的主目录

英文版的Windows的主目录通常是C:\Documents and Settings下你的用户名的那个文件夹。如果要找出你的主目录的确切位置,可以打开一个命令行窗口,运行以下命令。

C:\> echo %UserProfile%
# This is a Mercurial configuration file.
[ui]
username = Firstname Lastname <email.address@example.net>

配置文件中[ui]这行标识着一个字段的开始,username = ...这行的意思是在ui字段中设定项username的值。当出现一个新的字段或者到达文件结尾的时候,当前的字段才结束。

2.7.1.2. 选择用户名称

username配置项的值可以是你喜欢的任意文字,因为这项信息主要是给其他用户阅读的,所以Mercurial不会去解释它。大多数人的习惯是采用用户名加上邮件地址的格式,就像上面的例子一样。

[注意] 注意

Mercurial内置的网络服务器会混淆邮件地址,让垃圾邮件发送者用的邮件地址抓取工具很难获取你的邮件地址。当你把Mercurial的版本库在网路上发布的时候,这可以减小你接收到垃圾邮件的风险。

2.7.2. 写提交日志

我们提交一个变更的时候,Mercurial会打开一个编辑器,让我们输入一些信息来描述这个变更集做的更改。这就是提交日志。它会告诉读者我们改了什么以及修改的原因,我们提交之后,命令hg log会输出这些信息。

$ hg commit

hg commit命令打开的编辑器包括一两个空行,接着是以HG:开始的行。

This is where I type my commit comment.

HG: Enter commit message.  Lines beginning with 'HG:' are removed.
HG: --
HG: user: Bryan O'Sullivan <bos@serpentine.com>
HG: branch 'default'
HG: changed hello.c

Mercurial会忽略以HG:为开始的行;它仅仅用来告诉我们这个变更集中包括哪些文件。修改或者删除这行没有任何影响。

2.7.3. 写高质量的提交日志

因为hg log命令在缺省情况下仅会输出提交日志的第一行,所以日志第一行最好是单独的一行。下面是一个日志的实例,它没有遵守这个规则,因此摘要可读性很差。

changeset:   73:584af0e231be
user:        Censored Person <censored.person@example.org>
date:        Tue Sep 26 21:37:07 2006 -0700
summary:     include buildmeister/commondefs. Add exports.

至于日志的其他部分的内容,没有严格的规定。Mercurial并不解释或者关心日志的内容,虽然你的项目可能有某种格式的规定。

我个人喜欢简短,而又信息量大的日志,它能告诉我一些我不能通过快速浏览hg log --patch的输出而得到的信息。

如果我们运行hg commit命令的时候没有指定文件,它会提交我们做的所有修改,与hg statushg diff这两个命令的输出一样。

[注意] Subversion用户的困惑

和所有的Mercurial命令一样,如果我们不明确指出hg commit命令要提交的文件名,它会在整个版本库的工作目录上执行操作。如果你以前使用过CVS或者Subversion,那一定要注意,你可能仅仅希望提交当前目录与其子目录的修改。

2.7.4. 终止提交

如果你在编辑日志的时候决定不再提交,只要退出编辑器同时不保存文件就可以了。这对版本库和当前目录都没有任何影响。

2.7.5. 欣赏我们的成果

提交完成后,我们就可以用hg tip命令显示刚刚创建的变更集。这个命令和hg log的输出一样,但是只显示版本库中最新的版本。

$ hg tip -vp
changeset:   5:cfb10a77c108
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Fri Oct 23 01:38:05 2009 +0000
files:       hello.c
description:
Added an extra line of output


diff -r 2278160e78d4 -r cfb10a77c108 hello.c
--- a/hello.c	Sat Aug 16 22:16:53 2008 +0200
+++ b/hello.c	Fri Oct 23 01:38:05 2009 +0000
@@ -8,5 +8,6 @@
 int main(int argc, char **argv)
 {
 	printf("hello, world!\");
+	printf("hello again!\n");
 	return 0;
 }

我们通常把版本库中最新的版本称为tip版本或者简称为tip

顺便提一下,hg tip命令可以接受很多和hg log命令一样的选项。如-v选项的意思是详细的-p的意思是输出补丁。使用-p输出补丁也是我们前面提到的一致性的另外一个例子。

2.8. 分享修改

前面我们曾经提到Mercurial的版本库是自包含的。这意味着我们刚才创建的变更集仅仅存在于我们的my-hello版本库中。下面我们会看到几种将变更传播到其他版本库的方法。

2.8.1. 从其它版本库取得变更

首先,我们克隆原始版本的hello版本库,它不包含我们刚刚提交的变更。我们将这个临时版本库称为hello

$ cd ..
$ hg clone hello hello-pull
updating working directory
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

我们用hg pull命令将变更从my-hello拖到hello-pull。然而,不管三七二十一将不了解的变更拖进版本库也实在是冒险。Mercurial提供了hg incoming命令,它会告诉我们hg pull将会把哪些变更拖进版本库,但不会真正的执行。

$ cd hello-pull
$ hg incoming ../my-hello
comparing with ../my-hello
searching for changes
changeset:   5:cfb10a77c108
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Fri Oct 23 01:38:05 2009 +0000
summary:     Added an extra line of output

运行hg pull命令将变更拖进版本库非常简单,你可以指定从那个版本库拖变更。

$ hg tip
changeset:   4:2278160e78d4
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:16:53 2008 +0200
summary:     Trim comments.

$ hg pull ../my-hello
pulling from ../my-hello
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
(run 'hg update' to get a working copy)
$ hg tip
changeset:   5:cfb10a77c108
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Fri Oct 23 01:38:05 2009 +0000
summary:     Added an extra line of output

从前后的hg tip的输出可以看出,我们成功将变更拖进了我们的版本库。然而,Mercurial将拖变更和更新当前工作目录分成两个操作。在看到在当前目录中我们刚拖进的变更之前,还有一步要完成。

[提示] 提取指定的修改

因为在运行hg incominghg pull之间可能存在延时,你可能不能看到从其他版本库中的所有导入进来的变更集。假如你正在通过网络从其他地方的版本库拖变更。当你查看hg incoming的输出,还没有拖这些变更的时候,其他人向这个版本库提交了一些东西。这意味着你可能拖进来比你用hg incoming看见的多的变更。

如果你仅希望将hg incoming命令列出的变更拖进来,或者由于其他原因希望得到变更的一个子集,那么你可以明确的指定变更集的ID,比如hg pull -r7e95bb

2.8.2. 更新工作目录

现在我们已经对版本库和它的工作目录之间的关系有了粗略的了解。我们在第 2.8.1 节 “从其它版本库取得变更”一节运行的hg pull命令会将变更拖进版本库,但是如果我们检查一下的话,就会发现工作目录并没有变化。这是因为hg pull命令并不会影响工作目录。实际上,我们需要hg update命令来完成这个工作。

$ grep printf hello.c
	printf("hello, world!\");
$ hg update tip
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ grep printf hello.c
	printf("hello, world!\");
	printf("hello again!\n");

hg pull命令并不会自动更新工作目录,这看起来有点奇怪。但实际上这样做是有原因的:你可以用hg update来更新工作目录,切换到版本库中的任意一个版本。假设你将工作目录切换到一个老的版本—假如说是为了追踪一个bug—然后运行了hg pull命令。它自动将工作目录更新到新版本,这可能并不是你想要的结果。

因为拖然后更新是个非常常用的操作顺序,Mercurial允许你将这两个操作组合在一起,只要给hg pull命令加上-u选项就可以了。

如果回顾第 2.8.1 节 “从其它版本库取得变更”一节,我们运行hg pull而又没有加上-u选项时,你可能会发现它输出了一条很有用的提示,我们还需要执行一个操作,才能更新工作目录。

如果想知道工作目录的版本,可以使用hg parents命令。

$ hg parents
changeset:   5:cfb10a77c108
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Fri Oct 23 01:38:05 2009 +0000
summary:     Added an extra line of output

如果回顾图 2.1 “版本库 hello 的历史图”一节,你会看到箭头连着每个变更集。箭头离开的节点是父版本,箭头指向的是子版本。工作目录的父版本也是一样的方式;它是工作目录包含的变更集。

如果需要将工作目录切换到一个特定版本,给hg update命令加上版本号或者变更集标识符就可以了。

$ hg update 2
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg parents
changeset:   2:fef857204a0c
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Sat Aug 16 22:05:04 2008 +0200
summary:     Introduce a typo into hello.c.

$ hg update
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg parents
changeset:   5:cfb10a77c108
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Fri Oct 23 01:38:05 2009 +0000
summary:     Added an extra line of output

如果没有明确指出版本,hg update会更新到tip版,就像上面例子hg update第二次执行的结果一样。

2.8.3. 发布修改到其它版本库

我们可以将变更从当前所在的版本库推到其他的版本库。与上面的hgpull例子一样,我们可以创建一个临时的版本库存放我们的变更。

$ cd ..
$ hg clone hello hello-push
updating working directory
2 files updated, 0 files merged, 0 files removed, 0 files unresolved

hg outgoing命令可以告诉我们那些变更将会被推到另外一个版本库。

$ cd my-hello
$ hg outgoing ../hello-push
comparing with ../hello-push
searching for changes
changeset:   5:cfb10a77c108
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Fri Oct 23 01:38:05 2009 +0000
summary:     Added an extra line of output

hg push命令则会执行真正的推操作。

$ hg push ../hello-push
pushing to ../hello-push
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files

hg pull一样,在变更推送之后,hg push命令并不会更新版本库的工作目录,与hg pull命令不同,hg push并不提供-u选项来更新其他版本库的工作目录。这一不对称是有目的的:我们推送的版本库可能是一个远端的服务器,并且很多人共享使用它。如果在其他人正在工作的时候,我们更新了工作目录,那么他们的工作很可能被破坏。

如果我们向一个已经包含了这些变更的版本库推送或者拉这些变更会发生什么事情呢?,什么也不会发生。

$ hg push ../hello-push
pushing to ../hello-push
searching for changes
no changes found

2.8.4. 默认位置

在我们克隆版本库的时候,Mercurial会在新的版本库的.hg/hgrc文件中记录下版本库的位置,如果我们对hg pull没有指定来源,或者对于hg push 没有指定目标,那么这些命令就会使用缺省位置。hg incominghg outgoing命令也是如此。

如果你用文本编辑器打开版本库的.hg/hgrc文件,你会看到如下内容。

[paths]
default = http://www.selenic.com/repo/hg

有可能—并且常常很有用—hg pushhg outgoing的缺省位置与hg pullhg incoming的位置不同。我们可以给.hg/hgrc文件的[paths]节加上default-push条目,如下所示。

[paths]
default = http://www.selenic.com/repo/hg
default-push = http://hg.example.com/hg

2.8.5. 通过网络共享修改

前面几节我们介绍的命令不仅可以用于本地版本库,还可以用于网络;只要传递的参数从本地路径变成URL就可以了。

$ hg outgoing http://hg.serpentine.com/tutorial/hello
comparing with http://hg.serpentine.com/tutorial/hello
searching for changes
changeset:   5:cfb10a77c108
tag:         tip
user:        Bryan O'Sullivan <bos@serpentine.com>
date:        Fri Oct 23 01:38:05 2009 +0000
summary:     Added an extra line of output

在本例中,我们可以看到准备向远程版本库推送的变更,但是该版本库并不允许匿名用户推送。

$ hg push http://hg.serpentine.com/tutorial/hello
pushing to http://hg.serpentine.com/tutorial/hello
searching for changes
ssl required

2.9. 开始新项目

开始一个新项目和使用一个已有项目一样简单。hg init命令可以创建一个新的,空的Mercurial版本库。

$ hg init myproject

在当前目录创建一个名为myproject的版本库就是这么简单。

$ ls -l
total 8
-rw-r--r-- 1 dongsheng g11n 47 Oct 23 01:37 goodbye.c
-rw-r--r-- 1 dongsheng g11n 45 Oct 23 01:37 hello.c
drwxr-xr-x 3 dongsheng g11n 16 Oct 23 01:37 myproject

我们确信myproject是一个Mercurial版本库, 因为它包含了.hg目录。

$ ls -al myproject
total 0
drwxr-xr-x 3 dongsheng g11n 16 Oct 23 01:37 .
drwx------ 3 dongsheng g11n 78 Oct 23 01:37 ..
drwxr-xr-x 3 dongsheng g11n 53 Oct 23 01:37 .hg

如果想将一些已有的文件加入版本库,我们可以将它们拷贝到目录下,然后执行hg add命令,告诉Mercurial开始管理它们。

$ cd myproject
$ cp ../hello.c .
$ cp ../goodbye.c .
$ hg add
adding goodbye.c
adding hello.c
$ hg status
A goodbye.c
A hello.c

当对项目的状况满意时,我们就提交的变更。

$ hg commit -m 'Initial commit'

只需要几分钟就可以在一个新的项目上使用Mercurial,这时它的魅力之一。现在版本控制做起来非常方便,我们可以在很小的不需要复杂工具的项目上使用它。



[1] 如果版本库的源和目标都在同一个文件系统上,将会节省很多空间。这种情况下,Mercurial会使用硬链接的方式来共享内部元数据,并使用写时拷贝的机制。如果你不明白这句话的意思,没有关系:所有的事情都是自动和透明的,你不需要知道它们。